diff --git a/.gitignore b/.gitignore index bd6adef..d36cf32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,47 +1,43 @@ -.cursorrules -.giga/ +# Dependencies +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* -furps-comparison.md +# Build outputs +dist/ +build/ +*.tsbuildinfo +.tsbuildinfo -README-task-master.md -.cursor -scripts +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db # Logs logs *.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* -node_modules -dist -dist-ssr -*.local +# Coverage +coverage/ +.nyc_output/ -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? +# TypeScript cache +*.tsbuildinfo -# Added by Claude Task Master -dev-debug.log -# Dependency directories -node_modules/ -# Environment variables -.env -.vscode -# OS specific -# Task files -tasks.json -tasks/ - -IMPLEMENTATION_PLAN.md \ No newline at end of file +.giga +.cursor +.cursorrules \ No newline at end of file diff --git a/README.md b/README.md index 668e124..c0f7ae8 100644 --- a/README.md +++ b/README.md @@ -1,138 +1,72 @@ -# OpChan +# Opchan -A decentralized forum application built as a Proof of Concept for a Waku-powered discussion platform. OpChan enables users to create "cells" (discussion boards), make posts, and engage in threaded conversations using Bitcoin Ordinal verification and the Waku protocol for decentralized messaging. +A TypeScript browser library workspace. -## Quick Start +## Structure -### Prerequisites +This is an npm workspace containing: -- Node.js 18+ and npm -- [Phantom Wallet](https://phantom.app/) browser extension -- Bitcoin Ordinals (required for posting, optional for reading) +- `@opchan/core` - Core browser library package + +## Development ### Installation -1. **Clone the repository** - - ```bash - git clone https://github.com/waku-org/OpChan.git - cd OpChan - ``` - -2. **Install dependencies** - - ```bash - npm install - ``` - -3. **Setup environment variables** - - ```bash - cp .env.example .env - ``` - - Edit `.env` to configure development settings: - - ```env - # Set to 'true' to bypass verification in development - VITE_OPCHAN_MOCK_ORDINAL_CHECK=false - ``` - -4. **Start development server** - ```bash - npm run dev - ``` - -### Project Structure - +```bash +npm install ``` -src/ -├── components/ # React components -│ ├── ui/ # shadcn/ui component library -│ ├── ActivityFeed.tsx -│ ├── CellPage.tsx -│ ├── Dashboard.tsx -│ └── ... -├── contexts/ # React Context providers -│ ├── AuthContext.tsx # Wallet & authentication -│ ├── ForumContext.tsx # Forum data & state -│ └── forum/ # Forum logic modules -├── lib/ # Core libraries -│ ├── identity/ # Wallet & cryptographic operations -│ ├── waku/ # Waku protocol integration -│ └── utils.ts -├── pages/ # Route components -└── types/ # TypeScript definitions + +### Building + +Build all packages: +```bash +npm run build +``` + +Build specific package: +```bash +npm run build --workspace=@opchan/core +``` + +### Development Mode + +Watch mode for development: +```bash +npm run dev --workspace=@opchan/core +``` + +### Testing + +```bash +npm test +``` + +### Linting + +```bash +npm run lint ``` ## Usage -### Getting Started +```typescript +import { Opchan } from '@opchan/core'; -1. **Connect Wallet**: Click "Connect Wallet" and approve the Phantom wallet connection -2. **Verify Ordinals**: The app will check if your wallet contains Logos Operator Bitcoin Ordinals -3. **Browse Cells**: View existing discussion boards on the dashboard -4. **Create Content**: Create new cells, posts, or comments (requires Ordinals) -5. **Moderate**: Cell creators can moderate their boards +const opchan = new Opchan({ + debug: true, + version: '1.0.0' +}); -### Authentication Flow +console.log(opchan.getVersion()); // "1.0.0" +opchan.log('Hello from Opchan!'); // [Opchan] Hello from Opchan! +``` -OpChan uses a two-tier authentication system: +## Packages -1. **Wallet Connection**: Initial connection to Phantom wallet -2. **Key Delegation**: Optional browser key generation for improved UX - - Reduces wallet signature prompts - - Configurable duration: 1 week or 30 days - - Can be regenerated anytime +### @opchan/core -### Network & Performance +The core browser library providing the main functionality. -- **Waku Network**: Connects to multiple bootstrap nodes for resilience -- **Message Caching**: Local caching with IndexedDB (planned) -- **Time-bounded Queries**: 24-hour query windows to prevent database overload -- **Pagination**: 50 messages per query with fallback limits +## License -## Contributing - -### Development Setup - -1. Fork the repository -2. Create a feature branch: `git checkout -b feature/amazing-feature` -3. Make your changes following the existing code style -4. Test your changes thoroughly -5. Commit your changes: `git commit -m 'Add amazing feature'` -6. Push to the branch: `git push origin feature/amazing-feature` -7. Open a Pull Request - -## TODOs - -- [x] replace mock wallet connection/disconnection - - supports Phantom -- [x] replace mock Ordinal verification (API) -- [ ] figure out using actual icons for cells -- [ ] store message cache in indexedDB -- make app local-first (update from/to Waku when available) -- [ ] moderation - - [ ] admins can "moderate" comments/posts - -## Architecture - -OpChan implements a decentralized architecture with these key components: - -- **Waku Protocol**: Handles peer-to-peer messaging and content distribution -- **Bitcoin Ordinals**: Provides decentralized identity verification -- **Key Delegation**: Improves UX while maintaining security -- **Content Addressing**: Messages are cryptographically signed and verifiable -- **Moderation Layer**: Cell-based moderation without global censorship - -## Support - -For questions, issues, or contributions: - -- Open an issue on GitHub for bugs or feature requests -- Check existing issues before creating new ones -- Provide detailed information for bug reports -- Include steps to reproduce issues - ---- - -**Note**: This is a Proof of Concept implementation. Use at your own risk in production environments. +MIT \ No newline at end of file diff --git a/.env.example b/app/.env.example similarity index 100% rename from .env.example rename to app/.env.example diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..bd6adef --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,47 @@ +.cursorrules +.giga/ + +furps-comparison.md + +README-task-master.md +.cursor +scripts + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Added by Claude Task Master +dev-debug.log +# Dependency directories +node_modules/ +# Environment variables +.env +.vscode +# OS specific +# Task files +tasks.json +tasks/ + +IMPLEMENTATION_PLAN.md \ No newline at end of file diff --git a/.prettierignore b/app/.prettierignore similarity index 100% rename from .prettierignore rename to app/.prettierignore diff --git a/.prettierrc b/app/.prettierrc similarity index 100% rename from .prettierrc rename to app/.prettierrc diff --git a/app/README.md b/app/README.md new file mode 100644 index 0000000..668e124 --- /dev/null +++ b/app/README.md @@ -0,0 +1,138 @@ +# OpChan + +A decentralized forum application built as a Proof of Concept for a Waku-powered discussion platform. OpChan enables users to create "cells" (discussion boards), make posts, and engage in threaded conversations using Bitcoin Ordinal verification and the Waku protocol for decentralized messaging. + +## Quick Start + +### Prerequisites + +- Node.js 18+ and npm +- [Phantom Wallet](https://phantom.app/) browser extension +- Bitcoin Ordinals (required for posting, optional for reading) + +### Installation + +1. **Clone the repository** + + ```bash + git clone https://github.com/waku-org/OpChan.git + cd OpChan + ``` + +2. **Install dependencies** + + ```bash + npm install + ``` + +3. **Setup environment variables** + + ```bash + cp .env.example .env + ``` + + Edit `.env` to configure development settings: + + ```env + # Set to 'true' to bypass verification in development + VITE_OPCHAN_MOCK_ORDINAL_CHECK=false + ``` + +4. **Start development server** + ```bash + npm run dev + ``` + +### Project Structure + +``` +src/ +├── components/ # React components +│ ├── ui/ # shadcn/ui component library +│ ├── ActivityFeed.tsx +│ ├── CellPage.tsx +│ ├── Dashboard.tsx +│ └── ... +├── contexts/ # React Context providers +│ ├── AuthContext.tsx # Wallet & authentication +│ ├── ForumContext.tsx # Forum data & state +│ └── forum/ # Forum logic modules +├── lib/ # Core libraries +│ ├── identity/ # Wallet & cryptographic operations +│ ├── waku/ # Waku protocol integration +│ └── utils.ts +├── pages/ # Route components +└── types/ # TypeScript definitions +``` + +## Usage + +### Getting Started + +1. **Connect Wallet**: Click "Connect Wallet" and approve the Phantom wallet connection +2. **Verify Ordinals**: The app will check if your wallet contains Logos Operator Bitcoin Ordinals +3. **Browse Cells**: View existing discussion boards on the dashboard +4. **Create Content**: Create new cells, posts, or comments (requires Ordinals) +5. **Moderate**: Cell creators can moderate their boards + +### Authentication Flow + +OpChan uses a two-tier authentication system: + +1. **Wallet Connection**: Initial connection to Phantom wallet +2. **Key Delegation**: Optional browser key generation for improved UX + - Reduces wallet signature prompts + - Configurable duration: 1 week or 30 days + - Can be regenerated anytime + +### Network & Performance + +- **Waku Network**: Connects to multiple bootstrap nodes for resilience +- **Message Caching**: Local caching with IndexedDB (planned) +- **Time-bounded Queries**: 24-hour query windows to prevent database overload +- **Pagination**: 50 messages per query with fallback limits + +## Contributing + +### Development Setup + +1. Fork the repository +2. Create a feature branch: `git checkout -b feature/amazing-feature` +3. Make your changes following the existing code style +4. Test your changes thoroughly +5. Commit your changes: `git commit -m 'Add amazing feature'` +6. Push to the branch: `git push origin feature/amazing-feature` +7. Open a Pull Request + +## TODOs + +- [x] replace mock wallet connection/disconnection + - supports Phantom +- [x] replace mock Ordinal verification (API) +- [ ] figure out using actual icons for cells +- [ ] store message cache in indexedDB -- make app local-first (update from/to Waku when available) +- [ ] moderation + - [ ] admins can "moderate" comments/posts + +## Architecture + +OpChan implements a decentralized architecture with these key components: + +- **Waku Protocol**: Handles peer-to-peer messaging and content distribution +- **Bitcoin Ordinals**: Provides decentralized identity verification +- **Key Delegation**: Improves UX while maintaining security +- **Content Addressing**: Messages are cryptographically signed and verifiable +- **Moderation Layer**: Cell-based moderation without global censorship + +## Support + +For questions, issues, or contributions: + +- Open an issue on GitHub for bugs or feature requests +- Check existing issues before creating new ones +- Provide detailed information for bug reports +- Include steps to reproduce issues + +--- + +**Note**: This is a Proof of Concept implementation. Use at your own risk in production environments. diff --git a/components.json b/app/components.json similarity index 100% rename from components.json rename to app/components.json diff --git a/eslint.config.js b/app/eslint.config.js similarity index 88% rename from eslint.config.js rename to app/eslint.config.js index c5b5079..c6f4edf 100644 --- a/eslint.config.js +++ b/app/eslint.config.js @@ -4,6 +4,8 @@ import reactHooks from 'eslint-plugin-react-hooks'; import reactRefresh from 'eslint-plugin-react-refresh'; import tseslint from 'typescript-eslint'; +const tsconfigRootDir = new URL('.', import.meta.url).pathname; + export default tseslint.config( { ignores: ['dist'] }, { @@ -12,6 +14,9 @@ export default tseslint.config( languageOptions: { ecmaVersion: 2020, globals: globals.browser, + parserOptions: { + tsconfigRootDir, + }, }, plugins: { 'react-hooks': reactHooks, diff --git a/furps-report.md b/app/furps-report.md similarity index 100% rename from furps-report.md rename to app/furps-report.md diff --git a/furps.md b/app/furps.md similarity index 100% rename from furps.md rename to app/furps.md diff --git a/index.html b/app/index.html similarity index 100% rename from index.html rename to app/index.html diff --git a/app/package.json b/app/package.json new file mode 100644 index 0000000..382af2b --- /dev/null +++ b/app/package.json @@ -0,0 +1,110 @@ +{ + "name": "opchan", + "private": true, + "version": "1.0.0", + "description": "A decentralized forum built on Waku.", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "build:dev": "vite build --mode development", + "check": "tsc --project tsconfig.app.json --noEmit && tsc --project tsconfig.node.json --noEmit && eslint . --fix && prettier --write .", + "fix": "prettier --write . && eslint . --fix", + "preview": "vite preview", + "test": "vitest", + "test:ui": "vitest --ui" + }, + "dependencies": { + "@hookform/resolvers": "^3.9.0", + "@noble/ed25519": "^2.2.3", + "@noble/hashes": "^1.8.0", + "@opchan/core": "file:../packages/core", + "@opchan/react": "file:../packages/react", + "@radix-ui/react-accordion": "^1.2.0", + "@radix-ui/react-alert-dialog": "^1.1.1", + "@radix-ui/react-aspect-ratio": "^1.1.0", + "@radix-ui/react-avatar": "^1.1.0", + "@radix-ui/react-checkbox": "^1.1.1", + "@radix-ui/react-collapsible": "^1.1.0", + "@radix-ui/react-context-menu": "^2.2.1", + "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-hover-card": "^1.1.1", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-menubar": "^1.1.1", + "@radix-ui/react-navigation-menu": "^1.2.0", + "@radix-ui/react-popover": "^1.1.1", + "@radix-ui/react-progress": "^1.1.0", + "@radix-ui/react-radio-group": "^1.2.0", + "@radix-ui/react-scroll-area": "^1.1.0", + "@radix-ui/react-select": "^2.1.1", + "@radix-ui/react-separator": "^1.1.0", + "@radix-ui/react-slider": "^1.2.0", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.1.0", + "@radix-ui/react-tabs": "^1.1.0", + "@radix-ui/react-toast": "^1.2.1", + "@radix-ui/react-toggle": "^1.1.0", + "@radix-ui/react-toggle-group": "^1.1.0", + "@radix-ui/react-tooltip": "^1.1.4", + "@reown/appkit": "^1.7.17", + "@reown/appkit-adapter-bitcoin": "^1.7.17", + "@reown/appkit-adapter-wagmi": "^1.7.17", + "@reown/appkit-wallet-button": "^1.7.17", + "@tanstack/react-query": "^5.84.1", + "buffer": "^6.0.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.0.0", + "date-fns": "^3.6.0", + "embla-carousel-react": "^8.3.0", + "input-otp": "^1.2.4", + "lucide-react": "^0.462.0", + "next-themes": "^0.3.0", + "re-resizable": "6.11.2", + "react": "^18.3.1", + "react-day-picker": "^8.10.1", + "react-dom": "^18.3.1", + "react-hook-form": "^7.53.0", + "react-markdown": "^10.1.0", + "react-resizable-panels": "^2.1.3", + "react-router-dom": "^6.26.2", + "recharts": "^2.12.7", + "rehype-sanitize": "^6.0.0", + "remark-gfm": "^4.0.1", + "sonner": "^1.5.0", + "tailwind-merge": "^2.5.2", + "tailwindcss-animate": "^1.0.7", + "uuid": "^11.1.0", + "vaul": "^0.9.3", + "viem": "^2.37.6", + "wagmi": "^2.17.0", + "zod": "^3.23.8" + }, + "devDependencies": { + "@eslint/js": "^9.9.0", + "@tailwindcss/typography": "^0.5.16", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", + "@types/node": "^22.5.5", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@types/uuid": "^10.0.0", + "@vitejs/plugin-react-swc": "^3.11.0", + "@vitest/ui": "^3.2.4", + "autoprefixer": "^10.4.20", + "eslint": "^9.9.0", + "eslint-plugin-react-hooks": "^5.1.0-rc.0", + "eslint-plugin-react-refresh": "^0.4.9", + "globals": "^15.9.0", + "jsdom": "^26.1.0", + "postcss": "^8.4.47", + "prettier": "^3.6.2", + "tailwindcss": "^3.4.11", + "typescript": "^5.5.3", + "typescript-eslint": "^8.0.1", + "vite": "^5.4.1", + "vitest": "^3.2.4" + } +} diff --git a/postcss.config.js b/app/postcss.config.js similarity index 100% rename from postcss.config.js rename to app/postcss.config.js diff --git a/public/favicon.ico b/app/public/favicon.ico similarity index 100% rename from public/favicon.ico rename to app/public/favicon.ico diff --git a/public/placeholder.svg b/app/public/placeholder.svg similarity index 100% rename from public/placeholder.svg rename to app/public/placeholder.svg diff --git a/public/robots.txt b/app/public/robots.txt similarity index 100% rename from public/robots.txt rename to app/public/robots.txt diff --git a/src/App.css b/app/src/App.css similarity index 100% rename from src/App.css rename to app/src/App.css diff --git a/app/src/App.tsx b/app/src/App.tsx new file mode 100644 index 0000000..353a6c8 --- /dev/null +++ b/app/src/App.tsx @@ -0,0 +1,39 @@ +import { Toaster } from '@/components/ui/toaster'; +import { Toaster as Sonner } from '@/components/ui/sonner'; +import { TooltipProvider } from '@/components/ui/tooltip'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; +import CellPage from './pages/CellPage'; +import PostPage from './pages/PostPage'; +import NotFound from './pages/NotFound'; +import Dashboard from './pages/Dashboard'; +import Index from './pages/Index'; +import ProfilePage from './pages/ProfilePage'; +import BookmarksPage from './pages/BookmarksPage'; +import DebugPage from './pages/DebugPage'; + +// Create a client +const queryClient = new QueryClient(); + +const App = () => ( + + + + + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + +); + +export default App; diff --git a/src/components/ActivityFeed.tsx b/app/src/components/ActivityFeed.tsx similarity index 100% rename from src/components/ActivityFeed.tsx rename to app/src/components/ActivityFeed.tsx diff --git a/src/components/CellList.tsx b/app/src/components/CellList.tsx similarity index 93% rename from src/components/CellList.tsx rename to app/src/components/CellList.tsx index 576baa9..d3892d5 100644 --- a/src/components/CellList.tsx +++ b/app/src/components/CellList.tsx @@ -1,6 +1,6 @@ import { useState, useMemo } from 'react'; import { Link } from 'react-router-dom'; -import { useForumData, useForumActions, usePermissions } from '@/hooks'; +import { useContent, usePermissions } from '@/hooks'; import { Layout, MessageSquare, @@ -24,9 +24,9 @@ import { import { CypherImage } from './ui/CypherImage'; import { RelevanceIndicator } from './ui/relevance-indicator'; import { ModerationToggle } from './ui/moderation-toggle'; -import { sortCells, SortOption } from '@/lib/utils/sorting'; -import { Cell } from '@/types/forum'; -import { usePending } from '@/hooks/usePending'; +import { sortCells, SortOption } from '@/utils/sorting'; +import type { Cell } from '@opchan/core'; +import { useForum } from '@/hooks'; import { ShareButton } from './ui/ShareButton'; // Empty State Component @@ -76,7 +76,8 @@ const EmptyState: React.FC<{ canCreateCell: boolean }> = ({ // Separate component to properly use hooks const CellItem: React.FC<{ cell: Cell }> = ({ cell }) => { - const pending = usePending(cell.id); + const { content } = useForum(); + const isPending = content.pending.isPending(cell.id); return ( @@ -103,7 +104,7 @@ const CellItem: React.FC<{ cell: Cell }> = ({ cell }) => { /> )} - {pending.isPending && ( + {isPending && (
syncing… @@ -139,8 +140,8 @@ const CellItem: React.FC<{ cell: Cell }> = ({ cell }) => { }; const CellList = () => { - const { cellsWithStats, isInitialLoading } = useForumData(); - const { refreshData } = useForumActions(); + const { cellsWithStats } = useContent(); + const content = useContent(); const { canCreateCell } = usePermissions(); const [sortOption, setSortOption] = useState('relevance'); @@ -149,7 +150,7 @@ const CellList = () => { return sortCells(cellsWithStats, sortOption); }, [cellsWithStats, sortOption]); - if (isInitialLoading) { + if (!cellsWithStats.length) { return (
@@ -220,13 +221,13 @@ const CellList = () => { diff --git a/src/components/CommentCard.tsx b/app/src/components/CommentCard.tsx similarity index 75% rename from src/components/CommentCard.tsx rename to app/src/components/CommentCard.tsx index 7fde4ce..ed100cc 100644 --- a/src/components/CommentCard.tsx +++ b/app/src/components/CommentCard.tsx @@ -1,18 +1,12 @@ import React from 'react'; import { ArrowUp, ArrowDown, Clock, Shield, UserX } from 'lucide-react'; import { formatDistanceToNow } from 'date-fns'; -import { Comment } from '@/types/forum'; -import { - useForumActions, - usePermissions, - useUserVotes, - useCommentBookmark, -} from '@/hooks'; +import type { CommentMessage } from '@opchan/core'; import { Button } from '@/components/ui/button'; import { BookmarkButton } from '@/components/ui/bookmark-button'; import { AuthorDisplay } from '@/components/ui/author-display'; import { MarkdownRenderer } from '@/components/ui/markdown-renderer'; -import { usePending, usePendingVote } from '@/hooks/usePending'; +import { useContent, useForum, usePermissions } from '@/hooks'; import { Tooltip, TooltipContent, @@ -21,7 +15,7 @@ import { import { ShareButton } from '@/components/ui/ShareButton'; interface CommentCardProps { - comment: Comment; + comment: CommentMessage; postId: string; cellId?: string; canModerate: boolean; @@ -32,7 +26,8 @@ interface CommentCardProps { // Extracted child component to respect Rules of Hooks const PendingBadge: React.FC<{ id: string }> = ({ id }) => { - const { isPending } = usePending(id); + const { content } = useForum(); + const isPending = content.pending.isPending(id); if (!isPending) return null; return ( <> @@ -53,27 +48,35 @@ const CommentCard: React.FC = ({ onUnmoderateComment, onModerateUser, }) => { - const { voteComment, isVoting } = useForumActions(); - const { canVote } = usePermissions(); - const userVotes = useUserVotes(); - const { - isBookmarked, - loading: bookmarkLoading, - toggleBookmark, - } = useCommentBookmark(comment, postId); + const content = useContent(); + const permissions = usePermissions(); - const commentVotePending = usePendingVote(comment.id); + // Check if bookmarked + const isBookmarked = content.bookmarks.some( + b => b.targetId === comment.id && b.type === 'comment' + ); + const [bookmarkLoading, setBookmarkLoading] = React.useState(false); + + // Use library pending API + const commentVotePending = content.pending.isPending(comment.id); + + // Get user vote status from filtered comment data + const userUpvoted = Boolean((comment as unknown as { userUpvoted?: boolean }).userUpvoted); + const userDownvoted = Boolean((comment as unknown as { userDownvoted?: boolean }).userDownvoted); + const score = (comment as unknown as { voteScore?: number }).voteScore ?? 0; + const isModerated = Boolean((comment as unknown as { moderated?: boolean }).moderated); const handleVoteComment = async (isUpvote: boolean) => { - await voteComment(comment.id, isUpvote); + await content.vote({ targetId: comment.id, isUpvote }); }; const handleBookmark = async () => { - await toggleBookmark(); - }; - - const getCommentVoteType = () => { - return userVotes.getCommentVoteType(comment.id); + setBookmarkLoading(true); + try { + await content.toggleCommentBookmark(comment, postId); + } finally { + setBookmarkLoading(false); + } }; return ( @@ -82,30 +85,32 @@ const CommentCard: React.FC = ({
- {comment.voteScore} + {score} - {commentVotePending.isPending && ( + {commentVotePending && ( syncing… )}
@@ -151,7 +156,7 @@ const CommentCard: React.FC = ({
- {canModerate && !comment.moderated && ( + {canModerate && !isModerated && (
-
{displayName}
+
{currentUser?.displayName}
{ )} {verificationStatus === EVerificationStatus.WALLET_CONNECTED && ( -
- - Connected. You can post, comment, and vote. -
- )} +
+ + Connected. You can post, comment, and vote. +
+ )} )} diff --git a/src/components/Header.tsx b/app/src/components/Header.tsx similarity index 82% rename from src/components/Header.tsx rename to app/src/components/Header.tsx index bfc3a60..2b6c706 100644 --- a/src/components/Header.tsx +++ b/app/src/components/Header.tsx @@ -1,11 +1,8 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Link, useLocation } from 'react-router-dom'; -import { useAuth, useWakuHealthStatus } from '@/hooks'; -import { useAuth as useAuthContext } from '@/contexts/useAuth'; -import { EVerificationStatus } from '@/types/identity'; -import { useForum } from '@/contexts/useForum'; -import { localDatabase } from '@/lib/database/LocalDatabase'; -import { DelegationFullStatus } from '@/lib/delegation'; +import { useAuth, useForum, useNetwork, useUIState } from '@/hooks'; +import { EVerificationStatus } from '@opchan/core'; +import { localDatabase } from '@opchan/core'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; @@ -48,74 +45,35 @@ import { useToast } from '@/components/ui/use-toast'; import { useAppKitAccount, useDisconnect } from '@reown/appkit/react'; import { WalletWizard } from '@/components/ui/wallet-wizard'; -import { useUserDisplay } from '@/hooks'; import { WakuHealthDot } from '@/components/ui/waku-health-indicator'; const Header = () => { - const { verificationStatus } = useAuth(); - const { getDelegationStatus } = useAuthContext(); - const [delegationInfo, setDelegationInfo] = - useState(null); - const wakuHealth = useWakuHealthStatus(); - const location = useLocation(); - const { toast } = useToast(); - const forum = useForum(); + const { currentUser, delegationInfo } = useAuth(); + const {statusMessage} = useNetwork(); + + const location = useLocation() + const { toast } = useToast(); + const { content } = useForum(); - // Use AppKit hooks for multi-chain support const bitcoinAccount = useAppKitAccount({ namespace: 'bip122' }); const ethereumAccount = useAppKitAccount({ namespace: 'eip155' }); const { disconnect } = useDisconnect(); - // Determine which account is connected - const isBitcoinConnected = bitcoinAccount.isConnected; - const isEthereumConnected = ethereumAccount.isConnected; - const isConnected = isBitcoinConnected || isEthereumConnected; - const address = isConnected - ? isBitcoinConnected - ? bitcoinAccount.address - : ethereumAccount.address - : undefined; + const isConnected = bitcoinAccount.isConnected || ethereumAccount.isConnected; const [walletWizardOpen, setWalletWizardOpen] = useState(false); const [mobileMenuOpen, setMobileMenuOpen] = useState(false); - // ✅ Get display name from enhanced hook - const { displayName } = useUserDisplay(address || ''); - - // Load delegation status - React.useEffect(() => { - getDelegationStatus().then(setDelegationInfo).catch(console.error); - }, [getDelegationStatus]); - - // Use LocalDatabase to persist wizard state across navigation - const getHasShownWizard = async (): Promise => { - try { - const value = await localDatabase.loadUIState('hasShownWalletWizard'); - return value === true; - } catch { - return false; - } - }; - - const setHasShownWizard = async (value: boolean): Promise => { - try { - await localDatabase.storeUIState('hasShownWalletWizard', value); - } catch (e) { - console.error('Failed to store wizard state', e); - } - }; + // Use centralized UI state instead of direct LocalDatabase access + const [hasShownWizard, setHasShownWizard] = useUIState('hasShownWalletWizard', false); // Auto-open wizard when wallet connects for the first time React.useEffect(() => { - if (isConnected) { - getHasShownWizard().then(hasShown => { - if (!hasShown) { - setWalletWizardOpen(true); - setHasShownWizard(true).catch(console.error); - } - }); + if (isConnected && !hasShownWizard) { + setWalletWizardOpen(true); + setHasShownWizard(true); } - }, [isConnected]); + }, [isConnected, hasShownWizard, setHasShownWizard]); const handleConnect = async () => { setWalletWizardOpen(true); @@ -127,7 +85,7 @@ const Header = () => { const handleDisconnect = async () => { await disconnect(); - await setHasShownWizard(false); // Reset so wizard can show again on next connection + setHasShownWizard(false); // Reset so wizard can show again on next connection toast({ title: 'Wallet Disconnected', description: 'Your wallet has been disconnected successfully.', @@ -151,18 +109,23 @@ const Header = () => { } }; + + useEffect(() => { + console.log('currentUser', currentUser) + }, [currentUser]) + const getStatusIcon = () => { if (!isConnected) return ; if ( - verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED && + currentUser?.verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED && delegationInfo?.isValid ) { return ; - } else if (verificationStatus === EVerificationStatus.WALLET_CONNECTED) { + } else if (currentUser?.verificationStatus === EVerificationStatus.WALLET_CONNECTED) { return ; } else if ( - verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED + currentUser?.verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED ) { return ; } else { @@ -192,13 +155,13 @@ const Header = () => {
- {wakuHealth.statusMessage} + {statusMessage} - {forum.lastSync && ( + {content.lastSync && (
- {new Date(forum.lastSync).toLocaleTimeString([], { + {new Date(content.lastSync).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', })} @@ -222,11 +185,11 @@ const Header = () => { { > {getStatusIcon()} - {verificationStatus === EVerificationStatus.WALLET_UNCONNECTED + {currentUser?.verificationStatus === EVerificationStatus.WALLET_UNCONNECTED ? 'CONNECT' : delegationInfo?.isValid ? 'READY' - : verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED + : currentUser?.verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED ? 'EXPIRED' : 'DELEGATE'} @@ -252,7 +215,7 @@ const Header = () => { size="sm" className="flex items-center space-x-2 text-white hover:bg-cyber-muted/30" > -
{displayName}
+
{currentUser?.displayName}
@@ -260,15 +223,6 @@ const Header = () => { align="end" className="w-56 bg-black/95 border-cyber-muted/30" > -
-
- {displayName} -
-
- {address?.slice(0, 8)}...{address?.slice(-4)} -
-
- {
- {wakuHealth.statusMessage} - {forum.lastSync && ( + {statusMessage} + {content.lastSync && ( - {new Date(forum.lastSync).toLocaleTimeString([], { + {new Date(content.lastSync).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', })} diff --git a/src/components/PostCard.tsx b/app/src/components/PostCard.tsx similarity index 68% rename from src/components/PostCard.tsx rename to app/src/components/PostCard.tsx index 662e3e3..cffd9c9 100644 --- a/src/components/PostCard.tsx +++ b/app/src/components/PostCard.tsx @@ -2,64 +2,67 @@ import React from 'react'; import { Link } from 'react-router-dom'; import { ArrowUp, ArrowDown, MessageSquare } from 'lucide-react'; import { formatDistanceToNow } from 'date-fns'; -import { Post } from '@/types/forum'; -import { - useForumActions, - usePermissions, - useUserVotes, - useForumData, - usePostBookmark, -} from '@/hooks'; +import type { Post, PostMessage } from '@opchan/core'; import { RelevanceIndicator } from '@/components/ui/relevance-indicator'; import { AuthorDisplay } from '@/components/ui/author-display'; import { BookmarkButton } from '@/components/ui/bookmark-button'; import { LinkRenderer } from '@/components/ui/link-renderer'; -import { usePending, usePendingVote } from '@/hooks/usePending'; +import { useContent, usePermissions } from '@/hooks'; import { ShareButton } from '@/components/ui/ShareButton'; interface PostCardProps { - post: Post; + post: Post | PostMessage; commentCount?: number; } const PostCard: React.FC = ({ post, commentCount = 0 }) => { - const { cellsWithStats } = useForumData(); - const { votePost, isVoting } = useForumActions(); - const { canVote } = usePermissions(); - const userVotes = useUserVotes(); - const { - isBookmarked, - loading: bookmarkLoading, - toggleBookmark, - } = usePostBookmark(post, post.cellId); + const content = useContent(); + const permissions = usePermissions(); - // ✅ Get pre-computed cell data - const cell = cellsWithStats.find(c => c.id === post.cellId); + // Get cell data from content + const cell = content.cells.find((c) => c.id === post.cellId); const cellName = cell?.name || 'unknown'; - // ✅ Use pre-computed vote data (assuming post comes from useForumData) - const score = - 'voteScore' in post - ? (post.voteScore as number) - : post.upvotes.length - post.downvotes.length; - const { isPending } = usePending(post.id); - const votePending = usePendingVote(post.id); + // Use pre-computed vote data or safely compute from arrays when available + const computedVoteScore = + 'voteScore' in post && typeof (post as Post).voteScore === 'number' + ? (post as Post).voteScore + : undefined; + const upvoteCount = + 'upvotes' in post && Array.isArray((post as Post).upvotes) + ? (post as Post).upvotes.length + : 0; + const downvoteCount = + 'downvotes' in post && Array.isArray((post as Post).downvotes) + ? (post as Post).downvotes.length + : 0; + const score = computedVoteScore ?? upvoteCount - downvoteCount; - // ✅ Get user vote status from hook - const userVoteType = userVotes.getPostVoteType(post.id); - const userUpvoted = userVoteType === 'upvote'; - const userDownvoted = userVoteType === 'downvote'; + // Use library pending API + const isPending = content.pending.isPending(post.id); + + // Get user vote status from post data + const userUpvoted = + (post as unknown as { userUpvoted?: boolean }).userUpvoted || false; + const userDownvoted = + (post as unknown as { userDownvoted?: boolean }).userDownvoted || false; + + // Check if bookmarked + const isBookmarked = content.bookmarks.some((b) => b.targetId === post.id && b.type === 'post'); + const [bookmarkLoading, setBookmarkLoading] = React.useState(false); + + // Remove duplicate vote status logic // ✅ Content truncation (simple presentation logic is OK) + const contentText = typeof post.content === 'string' ? post.content : String(post.content ?? ''); const contentPreview = - post.content.length > 200 - ? post.content.substring(0, 200) + '...' - : post.content; + contentText.length > 200 + ? contentText.substring(0, 200) + '...' + : contentText; const handleVote = async (e: React.MouseEvent, isUpvote: boolean) => { e.preventDefault(); - // ✅ All validation and permission checking handled in hook - await votePost(post.id, isUpvote); + await content.vote({ targetId: post.id, isUpvote }); }; const handleBookmark = async (e?: React.MouseEvent) => { @@ -67,7 +70,12 @@ const PostCard: React.FC = ({ post, commentCount = 0 }) => { e.preventDefault(); e.stopPropagation(); } - await toggleBookmark(); + setBookmarkLoading(true); + try { + await content.togglePostBookmark(post, post.cellId); + } finally { + setBookmarkLoading(false); + } }; return ( @@ -82,8 +90,8 @@ const PostCard: React.FC = ({ post, commentCount = 0 }) => { : 'text-cyber-neutral hover:text-cyber-accent' }`} onClick={e => handleVote(e, true)} - disabled={!canVote || isVoting} - title={canVote ? 'Upvote' : 'Connect wallet and verify to vote'} + disabled={!permissions.canVote} + title={permissions.canVote ? 'Upvote' : permissions.reasons.vote} > @@ -107,12 +115,12 @@ const PostCard: React.FC = ({ post, commentCount = 0 }) => { : 'text-cyber-neutral hover:text-blue-400' }`} onClick={e => handleVote(e, false)} - disabled={!canVote || isVoting} - title={canVote ? 'Downvote' : 'Connect wallet and verify to vote'} + disabled={!permissions.canVote} + title={permissions.canVote ? 'Downvote' : permissions.reasons.vote} > - {votePending.isPending && ( + {isPending && ( syncing… )}
@@ -138,12 +146,12 @@ const PostCard: React.FC = ({ post, commentCount = 0 }) => { addSuffix: true, })} - {post.relevanceScore !== undefined && ( + {('relevanceScore' in post) && typeof (post as Post).relevanceScore === 'number' && ( <> { const { postId } = useParams<{ postId: string }>(); const navigate = useNavigate(); - // ✅ Use reactive hooks for data and actions - const post = usePost(postId); - const comments = usePostComments(postId); - const { - createComment, - votePost, - moderateComment, - unmoderateComment, - moderateUser, - isCreatingComment, - isVoting, - } = useForumActions(); - const { canVote, canComment, canModerate } = usePermissions(); - const userVotes = useUserVotes(); - const { - isBookmarked, - loading: bookmarkLoading, - toggleBookmark, - } = usePostBookmark(post, post?.cellId); + // Use aggregated forum API + const content = useContent(); + const permissions = usePermissions(); - // ✅ Move ALL hook calls to the top, before any conditional logic - const postPending = usePending(post?.id); - const postVotePending = usePendingVote(post?.id); + // Get post and comments using focused hooks + const post = content.posts.find((p) => p.id === postId); + const visibleComments = postId ? content.commentsByPost[postId] ?? [] : []; + + // Use library pending API + const postPending = content.pending.isPending(post?.id); + const postVotePending = content.pending.isPending(post?.id); + + // Check if bookmarked + const isBookmarked = content.bookmarks.some((b) => b.targetId === post?.id && b.type === 'post'); + const [bookmarkLoading, setBookmarkLoading] = React.useState(false); const [newComment, setNewComment] = useState(''); if (!postId) return
Invalid post ID
; // ✅ Loading state handled by hook - if (comments.isLoading) { + if (postPending) { return (
@@ -90,15 +72,14 @@ const PostDetail = () => { } // ✅ All data comes pre-computed from hooks - const { cell } = post; - const visibleComments = comments.comments; // Already filtered by hook + const cell = content.cells.find((c: ForumCell) => c.id === post?.cellId); const handleCreateComment = async (e: React.FormEvent) => { e.preventDefault(); if (!newComment.trim()) return; - // ✅ All validation handled in hook - const result = await createComment(postId, newComment); + // Use aggregated content API + const result = await content.createComment({ postId, content: newComment }); if (result) { setNewComment(''); } @@ -107,18 +88,18 @@ const PostDetail = () => { // Handle keyboard shortcuts const handleKeyDown = (e: React.KeyboardEvent) => { // Enter inserts newline by default. Send on Ctrl+Enter or Shift+Enter. - const isSendCombo = (e.ctrlKey || e.metaKey || e.shiftKey) && e.key === 'Enter'; + const isSendCombo = + (e.ctrlKey || e.metaKey || e.shiftKey) && e.key === 'Enter'; if (isSendCombo) { e.preventDefault(); - if (!isCreatingComment && newComment.trim()) { + if (newComment.trim()) { handleCreateComment(e as React.FormEvent); } } }; const handleVotePost = async (isUpvote: boolean) => { - // ✅ Permission checking handled in hook - await votePost(post.id, isUpvote); + await content.vote({ targetId: post.id, isUpvote }); }; const handleBookmark = async (e?: React.MouseEvent) => { @@ -126,35 +107,39 @@ const PostDetail = () => { e.preventDefault(); e.stopPropagation(); } - await toggleBookmark(); + setBookmarkLoading(true); + try { + await content.togglePostBookmark(post, post.cellId); + } finally { + setBookmarkLoading(false); + } }; - // ✅ Get vote status from hooks - const postVoteType = userVotes.getPostVoteType(post.id); - const isPostUpvoted = postVoteType === 'upvote'; - const isPostDownvoted = postVoteType === 'downvote'; + // Get vote status from post data (enhanced posts only) + const enhanced = post as unknown as { userUpvoted?: boolean; userDownvoted?: boolean; voteScore?: number }; + const isPostUpvoted = Boolean(enhanced.userUpvoted); + const isPostDownvoted = Boolean(enhanced.userDownvoted); + const score = typeof enhanced.voteScore === 'number' ? enhanced.voteScore : 0; const handleModerateComment = async (commentId: string) => { const reason = window.prompt('Enter a reason for moderation (optional):') || undefined; if (!cell) return; - // ✅ All validation handled in hook - await moderateComment(cell.id, commentId, reason); + await content.moderate.comment(cell.id, commentId, reason); }; const handleUnmoderateComment = async (commentId: string) => { const reason = window.prompt('Optional note for unmoderation?') || undefined; if (!cell) return; - await unmoderateComment(cell.id, commentId, reason); + await content.moderate.uncomment(cell.id, commentId, reason); }; const handleModerateUser = async (userAddress: string) => { const reason = window.prompt('Reason for moderating this user? (optional)') || undefined; if (!cell) return; - // ✅ All validation handled in hook - await moderateUser(cell.id, userAddress, reason); + await content.moderate.user(cell.id, userAddress, reason); }; return ( @@ -178,29 +163,29 @@ const PostDetail = () => { isPostUpvoted ? 'text-primary' : '' }`} onClick={() => handleVotePost(true)} - disabled={!canVote || isVoting} + disabled={!permissions.canVote} title={ - canVote ? 'Upvote post' : 'Connect wallet and verify to vote' + permissions.canVote ? 'Upvote post' : permissions.reasons.vote } > - {post.voteScore} + {score} - {postVotePending.isPending && ( + {postVotePending && ( syncing… @@ -226,19 +211,8 @@ const PostDetail = () => { addSuffix: true, })} - {post.relevanceScore !== undefined && ( - <> - - - - )} - {postPending.isPending && ( + {/* Relevance details unavailable in raw PostMessage; skip indicator */} + {postPending && ( <> @@ -273,7 +247,7 @@ const PostDetail = () => {
{/* Comment Form */} - {canComment && ( + {permissions.canComment && (

@@ -284,7 +258,7 @@ const PostDetail = () => { placeholder="What are your thoughts?" value={newComment} onChange={setNewComment} - disabled={isCreatingComment} + disabled={false} minHeight={100} initialHeight={140} maxHeight={600} @@ -292,30 +266,21 @@ const PostDetail = () => {

)} - {!canComment && ( + {!permissions.canComment && (

- Connect wallet and verify Ordinal ownership to comment + Connect your wallet to comment

) : ( - visibleComments.map(comment => ( + visibleComments.map((comment) => ( { const { cellId } = useParams<{ cellId: string }>(); // ✅ Use reactive hooks for data and actions - const cell = useCell(cellId); - const cellPosts = useCellPosts(cellId, { sortBy: 'relevance' }); - const { - createPost, - votePost, - moderatePost, - unmoderatePost, - moderateUser, - refreshData, - isCreatingPost, - isVoting, - } = useForumActions(); + const { createPost, vote, moderate, refresh, commentsByPost, cells, posts } = useContent(); + const cell = cells.find((c: ForumCell) => c.id === cellId); + const isCreatingPost = false; + const isVoting = false; const { canPost, canVote, canModerate } = usePermissions(); - const userVotes = useUserVotes(); const { currentUser } = useAuth(); - const { commentsByPost } = useForumData(); const [newPostTitle, setNewPostTitle] = useState(''); const [newPostContent, setNewPostContent] = useState(''); - if (!cellId || cellPosts.isLoading) { + if (!cellId) { return (
@@ -117,7 +101,7 @@ const PostList = () => { if (!newPostContent.trim()) return; // ✅ All validation handled in hook - const post = await createPost(cellId, newPostTitle, newPostContent); + const post = await createPost({ cellId, title: newPostTitle, content: newPostContent }); if (post) { setNewPostTitle(''); setNewPostContent(''); @@ -127,7 +111,8 @@ const PostList = () => { // Handle keyboard shortcuts const handleKeyDown = (e: React.KeyboardEvent) => { // Enter inserts newline by default. Send on Ctrl+Enter or Shift+Enter. - const isSendCombo = (e.ctrlKey || e.metaKey || e.shiftKey) && e.key === 'Enter'; + const isSendCombo = + (e.ctrlKey || e.metaKey || e.shiftKey) && e.key === 'Enter'; if (isSendCombo) { e.preventDefault(); if (!isCreatingPost && newPostContent.trim() && newPostTitle.trim()) { @@ -137,30 +122,40 @@ const PostList = () => { }; const handleVotePost = async (postId: string, isUpvote: boolean) => { - // ✅ Permission checking handled in hook - await votePost(postId, isUpvote); + await vote({ targetId: postId, isUpvote }); }; const getPostVoteType = (postId: string) => { - return userVotes.getPostVoteType(postId); + if (!currentUser) return null; + const p = posts.find((p: ForumPost) => p.id === postId); + if (!p) return null; + const up = p.upvotes.some((v: VoteMessage) => v.author === currentUser.address); + const down = p.downvotes.some((v: VoteMessage) => v.author === currentUser.address); + return up ? 'upvote' : down ? 'downvote' : null; }; // ✅ Posts already filtered by hook based on user permissions - const visiblePosts = cellPosts.posts; + const visiblePosts = posts + .filter((p: ForumPost) => p.cellId === cellId) + .sort((a: ForumPost, b: ForumPost) => { + const ar = a.relevanceScore ?? 0; + const br = b.relevanceScore ?? 0; + return br - ar || b.timestamp - a.timestamp; + }); const handleModerate = async (postId: string) => { const reason = window.prompt('Enter a reason for moderation (optional):') || undefined; if (!cell) return; // ✅ All validation handled in hook - await moderatePost(cell.id, postId, reason); + await moderate.post(cell.id, postId, reason); }; const handleUnmoderate = async (postId: string) => { const reason = window.prompt('Optional note for unmoderation?') || undefined; if (!cell) return; - await unmoderatePost(cell.id, postId, reason); + await moderate.unpost(cell.id, postId, reason); }; const handleModerateUser = async (userAddress: string) => { @@ -168,7 +163,7 @@ const PostList = () => { window.prompt('Reason for moderating this user? (optional)') || undefined; if (!cell) return; // ✅ All validation handled in hook - await moderateUser(cell.id, userAddress, reason); + await moderate.user(cell.id, userAddress, reason); }; return ( @@ -195,13 +190,11 @@ const PostList = () => {

{cell.description}

@@ -232,7 +225,9 @@ const PostList = () => { />
- Press Enter for newline • Ctrl+Enter or Shift+Enter to post + + Press Enter for newline • Ctrl+Enter or Shift+Enter to post +
@@ -251,11 +246,10 @@ const PostList = () => {
)} - {!canPost && !currentUser && (

- Connect wallet and verify Ordinal ownership to post + Connect your wallet to post

) : ( - visiblePosts.map(post => ( + visiblePosts.map((post: ForumPost) => (
@@ -284,7 +278,7 @@ const PostList = () => { onClick={() => handleVotePost(post.id, true)} disabled={!canVote || isVoting} title={ - canVote ? 'Upvote' : 'Connect wallet and verify to vote' + canVote ? 'Upvote' : 'Connect your wallet to vote' } > @@ -297,7 +291,7 @@ const PostList = () => { onClick={() => handleVotePost(post.id, false)} disabled={!canVote || isVoting} title={ - canVote ? 'Downvote' : 'Connect wallet and verify to vote' + canVote ? 'Downvote' : 'Connect your wallet to vote' } > @@ -329,6 +323,17 @@ const PostList = () => { {commentsByPost[post.id]?.length || 0} comments + {typeof post.relevanceScore === 'number' && ( + <> + + + + )} svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground', diff --git a/src/components/ui/aspect-ratio.tsx b/app/src/components/ui/aspect-ratio.tsx similarity index 100% rename from src/components/ui/aspect-ratio.tsx rename to app/src/components/ui/aspect-ratio.tsx diff --git a/src/components/ui/author-display.tsx b/app/src/components/ui/author-display.tsx similarity index 78% rename from src/components/ui/author-display.tsx rename to app/src/components/ui/author-display.tsx index 04b0bf7..f7623e7 100644 --- a/src/components/ui/author-display.tsx +++ b/app/src/components/ui/author-display.tsx @@ -1,6 +1,7 @@ import { Badge } from '@/components/ui/badge'; import { Shield, Crown, Hash } from 'lucide-react'; -import { useUserDisplay } from '@/hooks'; +import { useUserDisplay } from '@opchan/react'; +import { useEffect } from 'react'; interface AuthorDisplayProps { address: string; @@ -13,8 +14,11 @@ export function AuthorDisplay({ className = '', showBadge = true, }: AuthorDisplayProps) { - const { displayName, callSign, ensName, ordinalDetails } = - useUserDisplay(address); + const { ensName, ordinalDetails, callSign, displayName } = useUserDisplay(address); + + useEffect(()=> { + console.log({ensName, ordinalDetails, callSign, displayName, address}) + }, [address, ensName, ordinalDetails, callSign, displayName]) // Only show a badge if the author has ENS, Ordinal, or Call Sign const shouldShowBadge = showBadge && (ensName || ordinalDetails || callSign); diff --git a/src/components/ui/avatar.tsx b/app/src/components/ui/avatar.tsx similarity index 97% rename from src/components/ui/avatar.tsx rename to app/src/components/ui/avatar.tsx index dc95ec7..4606161 100644 --- a/src/components/ui/avatar.tsx +++ b/app/src/components/ui/avatar.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as AvatarPrimitive from '@radix-ui/react-avatar'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Avatar = React.forwardRef< React.ElementRef, diff --git a/src/components/ui/badge.tsx b/app/src/components/ui/badge.tsx similarity index 97% rename from src/components/ui/badge.tsx rename to app/src/components/ui/badge.tsx index 711f380..b347058 100644 --- a/src/components/ui/badge.tsx +++ b/app/src/components/ui/badge.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { cva, type VariantProps } from 'class-variance-authority'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const badgeVariants = cva( 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', diff --git a/src/components/ui/bookmark-button.tsx b/app/src/components/ui/bookmark-button.tsx similarity index 98% rename from src/components/ui/bookmark-button.tsx rename to app/src/components/ui/bookmark-button.tsx index 732231b..0c3623e 100644 --- a/src/components/ui/bookmark-button.tsx +++ b/app/src/components/ui/bookmark-button.tsx @@ -1,6 +1,6 @@ import { Button } from '@/components/ui/button'; import { Bookmark, BookmarkCheck } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' interface BookmarkButtonProps { isBookmarked: boolean; diff --git a/src/components/ui/bookmark-card.tsx b/app/src/components/ui/bookmark-card.tsx similarity index 97% rename from src/components/ui/bookmark-card.tsx rename to app/src/components/ui/bookmark-card.tsx index 13594f0..21dee46 100644 --- a/src/components/ui/bookmark-card.tsx +++ b/app/src/components/ui/bookmark-card.tsx @@ -8,9 +8,9 @@ import { Trash2, ExternalLink, } from 'lucide-react'; -import { Bookmark, BookmarkType } from '@/types/forum'; -import { useUserDisplay } from '@/hooks'; -import { cn } from '@/lib/utils'; +import { Bookmark, BookmarkType } from '@opchan/core'; +import { useUserDisplay } from '@opchan/react'; +import { cn } from '../../utils' import { formatDistanceToNow } from 'date-fns'; import { useNavigate } from 'react-router-dom'; diff --git a/src/components/ui/breadcrumb.tsx b/app/src/components/ui/breadcrumb.tsx similarity index 98% rename from src/components/ui/breadcrumb.tsx rename to app/src/components/ui/breadcrumb.tsx index 8b62197..eee54dd 100644 --- a/src/components/ui/breadcrumb.tsx +++ b/app/src/components/ui/breadcrumb.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Slot } from '@radix-ui/react-slot'; import { ChevronRight, MoreHorizontal } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Breadcrumb = React.forwardRef< HTMLElement, diff --git a/src/components/ui/button-variants.ts b/app/src/components/ui/button-variants.ts similarity index 100% rename from src/components/ui/button-variants.ts rename to app/src/components/ui/button-variants.ts diff --git a/src/components/ui/button.tsx b/app/src/components/ui/button.tsx similarity index 95% rename from src/components/ui/button.tsx rename to app/src/components/ui/button.tsx index e5c14e9..1db864b 100644 --- a/src/components/ui/button.tsx +++ b/app/src/components/ui/button.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Slot } from '@radix-ui/react-slot'; import { type VariantProps } from 'class-variance-authority'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' import { buttonVariants } from './button-variants'; export interface ButtonProps diff --git a/src/components/ui/calendar.tsx b/app/src/components/ui/calendar.tsx similarity index 98% rename from src/components/ui/calendar.tsx rename to app/src/components/ui/calendar.tsx index b9119bc..8c43da7 100644 --- a/src/components/ui/calendar.tsx +++ b/app/src/components/ui/calendar.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { ChevronLeft, ChevronRight } from 'lucide-react'; import { DayPicker } from 'react-day-picker'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' import { buttonVariants } from '@/components/ui/button-variants'; export type CalendarProps = React.ComponentProps; diff --git a/src/components/ui/call-sign-setup-dialog.tsx b/app/src/components/ui/call-sign-setup-dialog.tsx similarity index 95% rename from src/components/ui/call-sign-setup-dialog.tsx rename to app/src/components/ui/call-sign-setup-dialog.tsx index b68887c..28502ee 100644 --- a/src/components/ui/call-sign-setup-dialog.tsx +++ b/app/src/components/ui/call-sign-setup-dialog.tsx @@ -3,7 +3,8 @@ import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { Loader2, User, Hash } from 'lucide-react'; -import { useAuth, useUserActions, useForumActions } from '@/hooks'; +import { useAuth } from '@/hooks'; +import { useForum } from '@opchan/react'; import { Form, FormControl, @@ -30,7 +31,7 @@ import { SelectValue, } from '@/components/ui/select'; import { useToast } from '@/hooks/use-toast'; -import { EDisplayPreference } from '@/types/identity'; +import { EDisplayPreference } from '@opchan/core'; const formSchema = z.object({ callSign: z @@ -55,8 +56,9 @@ export function CallSignSetupDialog({ onOpenChange, }: CallSignSetupDialogProps = {}) { const { currentUser } = useAuth(); - const { updateProfile } = useUserActions(); - const { refreshData } = useForumActions(); + const forum = useForum(); + const { updateProfile } = forum.user; + const { refresh } = forum.content; const { toast } = useToast(); const [internalOpen, setInternalOpen] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); @@ -93,7 +95,7 @@ export function CallSignSetupDialog({ if (success) { // Refresh forum data to update user display - await refreshData(); + await refresh(); setOpen(false); form.reset(); } diff --git a/src/components/ui/card.tsx b/app/src/components/ui/card.tsx similarity index 98% rename from src/components/ui/card.tsx rename to app/src/components/ui/card.tsx index fca7be4..b8847c3 100644 --- a/src/components/ui/card.tsx +++ b/app/src/components/ui/card.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Card = React.forwardRef< HTMLDivElement, diff --git a/src/components/ui/carousel.tsx b/app/src/components/ui/carousel.tsx similarity index 99% rename from src/components/ui/carousel.tsx rename to app/src/components/ui/carousel.tsx index 2abee3e..8d452da 100644 --- a/src/components/ui/carousel.tsx +++ b/app/src/components/ui/carousel.tsx @@ -4,7 +4,7 @@ import useEmblaCarousel, { } from 'embla-carousel-react'; import { ArrowLeft, ArrowRight } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' import { Button } from '@/components/ui/button'; type CarouselApi = UseEmblaCarouselType[1]; diff --git a/src/components/ui/chart.tsx b/app/src/components/ui/chart.tsx similarity index 99% rename from src/components/ui/chart.tsx rename to app/src/components/ui/chart.tsx index 642d78b..37391ab 100644 --- a/src/components/ui/chart.tsx +++ b/app/src/components/ui/chart.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as RechartsPrimitive from 'recharts'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' // Format: { THEME_NAME: CSS_SELECTOR } const THEMES = { light: '', dark: '.dark' } as const; diff --git a/src/components/ui/checkbox.tsx b/app/src/components/ui/checkbox.tsx similarity index 96% rename from src/components/ui/checkbox.tsx rename to app/src/components/ui/checkbox.tsx index e9f5492..35548e4 100644 --- a/src/components/ui/checkbox.tsx +++ b/app/src/components/ui/checkbox.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; import { Check } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Checkbox = React.forwardRef< React.ElementRef, diff --git a/src/components/ui/collapsible.tsx b/app/src/components/ui/collapsible.tsx similarity index 100% rename from src/components/ui/collapsible.tsx rename to app/src/components/ui/collapsible.tsx diff --git a/src/components/ui/command.tsx b/app/src/components/ui/command.tsx similarity index 99% rename from src/components/ui/command.tsx rename to app/src/components/ui/command.tsx index 090bcb1..0986715 100644 --- a/src/components/ui/command.tsx +++ b/app/src/components/ui/command.tsx @@ -3,7 +3,7 @@ import { type DialogProps } from '@radix-ui/react-dialog'; import { Command as CommandPrimitive } from 'cmdk'; import { Search } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' import { Dialog, DialogContent } from '@/components/ui/dialog'; const Command = React.forwardRef< diff --git a/src/components/ui/context-menu.tsx b/app/src/components/ui/context-menu.tsx similarity index 99% rename from src/components/ui/context-menu.tsx rename to app/src/components/ui/context-menu.tsx index 9cd423b..ea200c1 100644 --- a/src/components/ui/context-menu.tsx +++ b/app/src/components/ui/context-menu.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import * as ContextMenuPrimitive from '@radix-ui/react-context-menu'; import { Check, ChevronRight, Circle } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const ContextMenu = ContextMenuPrimitive.Root; diff --git a/src/components/ui/delegation-step.tsx b/app/src/components/ui/delegation-step.tsx similarity index 86% rename from src/components/ui/delegation-step.tsx rename to app/src/components/ui/delegation-step.tsx index b521e4c..3cfc9c0 100644 --- a/src/components/ui/delegation-step.tsx +++ b/app/src/components/ui/delegation-step.tsx @@ -1,9 +1,8 @@ -import React, { useState, useEffect } from 'react'; +import React from 'react'; import { Button } from './button'; -import { useAuth, useAuthActions } from '@/hooks'; -import { useAuth as useAuthContext } from '@/contexts/useAuth'; +import { useAuth } from '@opchan/react'; import { CheckCircle, AlertCircle, Trash2 } from 'lucide-react'; -import { DelegationDuration, DelegationFullStatus } from '@/lib/delegation'; +import { DelegationDuration } from '@opchan/core'; interface DelegationStepProps { onComplete: () => void; @@ -18,16 +17,7 @@ export function DelegationStep({ isLoading, setIsLoading, }: DelegationStepProps) { - const { currentUser, isAuthenticating } = useAuth(); - const { getDelegationStatus } = useAuthContext(); - const [delegationInfo, setDelegationInfo] = - useState(null); - const { delegateKey, clearDelegation } = useAuthActions(); - - // Load delegation status - useEffect(() => { - getDelegationStatus().then(setDelegationInfo).catch(console.error); - }, [getDelegationStatus]); + const { currentUser, delegationInfo, delegate, clearDelegation } = useAuth(); const [selectedDuration, setSelectedDuration] = React.useState('7days'); @@ -44,11 +34,11 @@ export function DelegationStep({ setDelegationResult(null); try { - const success = await delegateKey(selectedDuration); + const success = await delegate(selectedDuration); if (success) { - const expiryDate = currentUser.delegationExpiry - ? new Date(currentUser.delegationExpiry).toLocaleString() + const expiryDate = delegationInfo?.expiresAt + ? delegationInfo.expiresAt.toLocaleString() : `${selectedDuration === '7days' ? '1 week' : '30 days'} from now`; setDelegationResult({ @@ -204,7 +194,7 @@ export function DelegationStep({ {/* User Address */} {currentUser && (
-
{currentUser.address}
+
{currentUser.displayName}
)} @@ -213,11 +203,7 @@ export function DelegationStep({
diff --git a/src/components/ui/dialog.tsx b/app/src/components/ui/dialog.tsx similarity index 99% rename from src/components/ui/dialog.tsx rename to app/src/components/ui/dialog.tsx index 22fa4f6..5fa0154 100644 --- a/src/components/ui/dialog.tsx +++ b/app/src/components/ui/dialog.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import * as DialogPrimitive from '@radix-ui/react-dialog'; import { X } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Dialog = DialogPrimitive.Root; diff --git a/src/components/ui/drawer.tsx b/app/src/components/ui/drawer.tsx similarity index 98% rename from src/components/ui/drawer.tsx rename to app/src/components/ui/drawer.tsx index cdd531d..927c148 100644 --- a/src/components/ui/drawer.tsx +++ b/app/src/components/ui/drawer.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Drawer as DrawerPrimitive } from 'vaul'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Drawer = ({ shouldScaleBackground = true, diff --git a/src/components/ui/dropdown-menu.tsx b/app/src/components/ui/dropdown-menu.tsx similarity index 99% rename from src/components/ui/dropdown-menu.tsx rename to app/src/components/ui/dropdown-menu.tsx index 1738b82..7711c34 100644 --- a/src/components/ui/dropdown-menu.tsx +++ b/app/src/components/ui/dropdown-menu.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; import { Check, ChevronRight, Circle } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const DropdownMenu = DropdownMenuPrimitive.Root; diff --git a/src/components/ui/form.tsx b/app/src/components/ui/form.tsx similarity index 99% rename from src/components/ui/form.tsx rename to app/src/components/ui/form.tsx index 02499b7..71e9787 100644 --- a/src/components/ui/form.tsx +++ b/app/src/components/ui/form.tsx @@ -10,7 +10,7 @@ import { useFormContext, } from 'react-hook-form'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' import { Label } from '@/components/ui/label'; const Form = FormProvider; diff --git a/src/components/ui/hover-card.tsx b/app/src/components/ui/hover-card.tsx similarity index 97% rename from src/components/ui/hover-card.tsx rename to app/src/components/ui/hover-card.tsx index 7b52752..bc5502a 100644 --- a/src/components/ui/hover-card.tsx +++ b/app/src/components/ui/hover-card.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as HoverCardPrimitive from '@radix-ui/react-hover-card'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const HoverCard = HoverCardPrimitive.Root; diff --git a/src/components/ui/input-otp.tsx b/app/src/components/ui/input-otp.tsx similarity index 98% rename from src/components/ui/input-otp.tsx rename to app/src/components/ui/input-otp.tsx index dbff250..2881012 100644 --- a/src/components/ui/input-otp.tsx +++ b/app/src/components/ui/input-otp.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { OTPInput, OTPInputContext } from 'input-otp'; import { Dot } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const InputOTP = React.forwardRef< React.ElementRef, diff --git a/src/components/ui/input.tsx b/app/src/components/ui/input.tsx similarity index 95% rename from src/components/ui/input.tsx rename to app/src/components/ui/input.tsx index 3de1e6a..a19b266 100644 --- a/src/components/ui/input.tsx +++ b/app/src/components/ui/input.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Input = React.forwardRef>( ({ className, type, ...props }, ref) => { diff --git a/src/components/ui/label.tsx b/app/src/components/ui/label.tsx similarity index 95% rename from src/components/ui/label.tsx rename to app/src/components/ui/label.tsx index fba3aeb..6cfbde3 100644 --- a/src/components/ui/label.tsx +++ b/app/src/components/ui/label.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import * as LabelPrimitive from '@radix-ui/react-label'; import { cva, type VariantProps } from 'class-variance-authority'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const labelVariants = cva( 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70' diff --git a/src/components/ui/link-renderer.tsx b/app/src/components/ui/link-renderer.tsx similarity index 100% rename from src/components/ui/link-renderer.tsx rename to app/src/components/ui/link-renderer.tsx diff --git a/src/components/ui/markdown-input.tsx b/app/src/components/ui/markdown-input.tsx similarity index 94% rename from src/components/ui/markdown-input.tsx rename to app/src/components/ui/markdown-input.tsx index 38fd5b8..5ded961 100644 --- a/src/components/ui/markdown-input.tsx +++ b/app/src/components/ui/markdown-input.tsx @@ -62,7 +62,10 @@ export const MarkdownInput: React.FC = ({
- +
@@ -71,5 +74,3 @@ export const MarkdownInput: React.FC = ({ }; export default MarkdownInput; - - diff --git a/src/components/ui/markdown-renderer.tsx b/app/src/components/ui/markdown-renderer.tsx similarity index 83% rename from src/components/ui/markdown-renderer.tsx rename to app/src/components/ui/markdown-renderer.tsx index 11fecd3..4ed854b 100644 --- a/src/components/ui/markdown-renderer.tsx +++ b/app/src/components/ui/markdown-renderer.tsx @@ -11,9 +11,12 @@ interface MarkdownRendererProps { /** * Renders sanitized Markdown with GFM support. */ -export const MarkdownRenderer: React.FC = ({ content, className }) => { +export const MarkdownRenderer: React.FC = ({ + content, + className, +}) => { // Extend sanitize schema to allow common markdown elements (headings, lists, code, tables, etc.) - const schema: any = { + const schema: typeof defaultSchema = { ...defaultSchema, tagNames: [ ...(defaultSchema.tagNames || []), @@ -57,15 +60,15 @@ export const MarkdownRenderer: React.FC = ({ content, cla ['alt'], ['title'], ], - code: [ - ...(defaultSchema.attributes?.code || []), - ['className'], - ], + code: [...(defaultSchema.attributes?.code || []), ['className']], }, }; return (
- + {content || ''}
@@ -73,5 +76,3 @@ export const MarkdownRenderer: React.FC = ({ content, cla }; export default MarkdownRenderer; - - diff --git a/src/components/ui/menubar.tsx b/app/src/components/ui/menubar.tsx similarity index 99% rename from src/components/ui/menubar.tsx rename to app/src/components/ui/menubar.tsx index 9c326ef..b61778f 100644 --- a/src/components/ui/menubar.tsx +++ b/app/src/components/ui/menubar.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import * as MenubarPrimitive from '@radix-ui/react-menubar'; import { Check, ChevronRight, Circle } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const MenubarMenu = MenubarPrimitive.Menu; diff --git a/src/components/ui/moderation-toggle.tsx b/app/src/components/ui/moderation-toggle.tsx similarity index 77% rename from src/components/ui/moderation-toggle.tsx rename to app/src/components/ui/moderation-toggle.tsx index 3495f83..ac98d87 100644 --- a/src/components/ui/moderation-toggle.tsx +++ b/app/src/components/ui/moderation-toggle.tsx @@ -1,14 +1,15 @@ import { Switch } from '@/components/ui/switch'; import { Label } from '@/components/ui/label'; import { Eye, EyeOff } from 'lucide-react'; -import { useModeration } from '@/contexts/ModerationContext'; -import { usePermissions } from '@/hooks/core/usePermissions'; -import { useForumData } from '@/hooks/core/useForumData'; +import React from 'react'; +import { usePermissions, useContent, useUIState } from '@/hooks'; export function ModerationToggle() { - const { showModerated, toggleShowModerated } = useModeration(); const { canModerate } = usePermissions(); - const { cellsWithStats } = useForumData(); + const { cellsWithStats } = useContent(); + + const [showModerated, setShowModerated] = useUIState('showModerated', false); + const toggleShowModerated = React.useCallback((value: boolean) => setShowModerated(value), [setShowModerated]); // Check if user is admin of any cell const isAdminOfAnyCell = cellsWithStats.some(cell => canModerate(cell.id)); diff --git a/src/components/ui/navigation-menu.tsx b/app/src/components/ui/navigation-menu.tsx similarity index 99% rename from src/components/ui/navigation-menu.tsx rename to app/src/components/ui/navigation-menu.tsx index 78b696f..b0c9afb 100644 --- a/src/components/ui/navigation-menu.tsx +++ b/app/src/components/ui/navigation-menu.tsx @@ -3,7 +3,7 @@ import * as NavigationMenuPrimitive from '@radix-ui/react-navigation-menu'; import { cva } from 'class-variance-authority'; import { ChevronDown } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const NavigationMenu = React.forwardRef< React.ElementRef, diff --git a/src/components/ui/pagination.tsx b/app/src/components/ui/pagination.tsx similarity index 98% rename from src/components/ui/pagination.tsx rename to app/src/components/ui/pagination.tsx index 5457502..859c3f1 100644 --- a/src/components/ui/pagination.tsx +++ b/app/src/components/ui/pagination.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { ChevronLeft, ChevronRight, MoreHorizontal } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' import { ButtonProps } from '@/components/ui/button'; import { buttonVariants } from '@/components/ui/button-variants'; diff --git a/src/components/ui/popover.tsx b/app/src/components/ui/popover.tsx similarity index 97% rename from src/components/ui/popover.tsx rename to app/src/components/ui/popover.tsx index fbc7adc..f40de05 100644 --- a/src/components/ui/popover.tsx +++ b/app/src/components/ui/popover.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as PopoverPrimitive from '@radix-ui/react-popover'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Popover = PopoverPrimitive.Root; diff --git a/src/components/ui/progress.tsx b/app/src/components/ui/progress.tsx similarity index 95% rename from src/components/ui/progress.tsx rename to app/src/components/ui/progress.tsx index e684ece..e5bac67 100644 --- a/src/components/ui/progress.tsx +++ b/app/src/components/ui/progress.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as ProgressPrimitive from '@radix-ui/react-progress'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Progress = React.forwardRef< React.ElementRef, diff --git a/src/components/ui/radio-group.tsx b/app/src/components/ui/radio-group.tsx similarity index 97% rename from src/components/ui/radio-group.tsx rename to app/src/components/ui/radio-group.tsx index 21fa8fd..1ad251e 100644 --- a/src/components/ui/radio-group.tsx +++ b/app/src/components/ui/radio-group.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import * as RadioGroupPrimitive from '@radix-ui/react-radio-group'; import { Circle } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const RadioGroup = React.forwardRef< React.ElementRef, diff --git a/src/components/ui/relevance-indicator.tsx b/app/src/components/ui/relevance-indicator.tsx similarity index 99% rename from src/components/ui/relevance-indicator.tsx rename to app/src/components/ui/relevance-indicator.tsx index 4858c66..e8bbad3 100644 --- a/src/components/ui/relevance-indicator.tsx +++ b/app/src/components/ui/relevance-indicator.tsx @@ -23,7 +23,7 @@ import { MessageSquare, ThumbsUp, } from 'lucide-react'; -import { RelevanceScoreDetails } from '@/types/forum'; +import { RelevanceScoreDetails } from '@opchan/core'; interface RelevanceIndicatorProps { score: number; diff --git a/src/components/ui/resizable-textarea.tsx b/app/src/components/ui/resizable-textarea.tsx similarity index 83% rename from src/components/ui/resizable-textarea.tsx rename to app/src/components/ui/resizable-textarea.tsx index 19019fa..2f5c3a8 100644 --- a/src/components/ui/resizable-textarea.tsx +++ b/app/src/components/ui/resizable-textarea.tsx @@ -1,14 +1,15 @@ import * as React from 'react'; import { Resizable } from 're-resizable'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' import { Textarea } from '@/components/ui/textarea'; -type ResizableTextareaProps = React.TextareaHTMLAttributes & { - initialHeight?: number; - minHeight?: number; - maxHeight?: number; -}; +type ResizableTextareaProps = + React.TextareaHTMLAttributes & { + initialHeight?: number; + minHeight?: number; + maxHeight?: number; + }; export const ResizableTextarea = React.forwardRef< HTMLTextAreaElement, @@ -44,7 +45,9 @@ export const ResizableTextarea = React.forwardRef< minHeight={minHeight} maxHeight={maxHeight} onResizeStop={(_event, _dir, _elementRef, delta) => { - setHeight(current => Math.max(minHeight, Math.min(maxHeight, current + delta.height))); + setHeight(current => + Math.max(minHeight, Math.min(maxHeight, current + delta.height)) + ); }} handleComponent={{ bottom: ( @@ -71,5 +74,3 @@ export const ResizableTextarea = React.forwardRef< ResizableTextarea.displayName = 'ResizableTextarea'; export default ResizableTextarea; - - diff --git a/src/components/ui/resizable.tsx b/app/src/components/ui/resizable.tsx similarity index 98% rename from src/components/ui/resizable.tsx rename to app/src/components/ui/resizable.tsx index 43c3162..1ad9455 100644 --- a/src/components/ui/resizable.tsx +++ b/app/src/components/ui/resizable.tsx @@ -1,7 +1,7 @@ import { GripVertical } from 'lucide-react'; import * as ResizablePrimitive from 'react-resizable-panels'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const ResizablePanelGroup = ({ className, diff --git a/src/components/ui/scroll-area.tsx b/app/src/components/ui/scroll-area.tsx similarity index 97% rename from src/components/ui/scroll-area.tsx rename to app/src/components/ui/scroll-area.tsx index 6b9cf48..7126250 100644 --- a/src/components/ui/scroll-area.tsx +++ b/app/src/components/ui/scroll-area.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const ScrollArea = React.forwardRef< React.ElementRef, diff --git a/src/components/ui/select.tsx b/app/src/components/ui/select.tsx similarity index 99% rename from src/components/ui/select.tsx rename to app/src/components/ui/select.tsx index c589424..74f8592 100644 --- a/src/components/ui/select.tsx +++ b/app/src/components/ui/select.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import * as SelectPrimitive from '@radix-ui/react-select'; import { Check, ChevronDown, ChevronUp } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Select = SelectPrimitive.Root; diff --git a/src/components/ui/separator.tsx b/app/src/components/ui/separator.tsx similarity index 95% rename from src/components/ui/separator.tsx rename to app/src/components/ui/separator.tsx index aca42e9..d8b4836 100644 --- a/src/components/ui/separator.tsx +++ b/app/src/components/ui/separator.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as SeparatorPrimitive from '@radix-ui/react-separator'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Separator = React.forwardRef< React.ElementRef, diff --git a/src/components/ui/sheet.tsx b/app/src/components/ui/sheet.tsx similarity index 99% rename from src/components/ui/sheet.tsx rename to app/src/components/ui/sheet.tsx index 0362b10..0596423 100644 --- a/src/components/ui/sheet.tsx +++ b/app/src/components/ui/sheet.tsx @@ -3,7 +3,7 @@ import { cva, type VariantProps } from 'class-variance-authority'; import { X } from 'lucide-react'; import * as React from 'react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Sheet = SheetPrimitive.Root; diff --git a/src/components/ui/sidebar.tsx b/app/src/components/ui/sidebar.tsx similarity index 99% rename from src/components/ui/sidebar.tsx rename to app/src/components/ui/sidebar.tsx index 97c6e4c..f6cca4c 100644 --- a/src/components/ui/sidebar.tsx +++ b/app/src/components/ui/sidebar.tsx @@ -4,7 +4,7 @@ import { VariantProps, cva } from 'class-variance-authority'; import { PanelLeft } from 'lucide-react'; import { useIsMobile } from '@/hooks/use-mobile'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Separator } from '@/components/ui/separator'; diff --git a/src/components/ui/skeleton.tsx b/app/src/components/ui/skeleton.tsx similarity index 87% rename from src/components/ui/skeleton.tsx rename to app/src/components/ui/skeleton.tsx index a626d9b..ec1d034 100644 --- a/src/components/ui/skeleton.tsx +++ b/app/src/components/ui/skeleton.tsx @@ -1,4 +1,4 @@ -import { cn } from '@/lib/utils'; +import { cn } from '../../utils'; function Skeleton({ className, diff --git a/src/components/ui/slider.tsx b/app/src/components/ui/slider.tsx similarity index 96% rename from src/components/ui/slider.tsx rename to app/src/components/ui/slider.tsx index 49ccd3c..6fd4b80 100644 --- a/src/components/ui/slider.tsx +++ b/app/src/components/ui/slider.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as SliderPrimitive from '@radix-ui/react-slider'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils'; const Slider = React.forwardRef< React.ElementRef, diff --git a/src/components/ui/sonner.tsx b/app/src/components/ui/sonner.tsx similarity index 100% rename from src/components/ui/sonner.tsx rename to app/src/components/ui/sonner.tsx diff --git a/src/components/ui/switch.tsx b/app/src/components/ui/switch.tsx similarity index 97% rename from src/components/ui/switch.tsx rename to app/src/components/ui/switch.tsx index 194422f..811f207 100644 --- a/src/components/ui/switch.tsx +++ b/app/src/components/ui/switch.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as SwitchPrimitives from '@radix-ui/react-switch'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Switch = React.forwardRef< React.ElementRef, diff --git a/src/components/ui/table.tsx b/app/src/components/ui/table.tsx similarity index 98% rename from src/components/ui/table.tsx rename to app/src/components/ui/table.tsx index 33a1209..ba596a4 100644 --- a/src/components/ui/table.tsx +++ b/app/src/components/ui/table.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Table = React.forwardRef< HTMLTableElement, diff --git a/src/components/ui/tabs.tsx b/app/src/components/ui/tabs.tsx similarity index 98% rename from src/components/ui/tabs.tsx rename to app/src/components/ui/tabs.tsx index 4a2b37f..cff9818 100644 --- a/src/components/ui/tabs.tsx +++ b/app/src/components/ui/tabs.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as TabsPrimitive from '@radix-ui/react-tabs'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Tabs = TabsPrimitive.Root; diff --git a/src/components/ui/textarea.tsx b/app/src/components/ui/textarea.tsx similarity index 95% rename from src/components/ui/textarea.tsx rename to app/src/components/ui/textarea.tsx index 7ffe9f6..9fd3984 100644 --- a/src/components/ui/textarea.tsx +++ b/app/src/components/ui/textarea.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const Textarea = React.forwardRef< HTMLTextAreaElement, diff --git a/src/components/ui/toast.tsx b/app/src/components/ui/toast.tsx similarity index 99% rename from src/components/ui/toast.tsx rename to app/src/components/ui/toast.tsx index b599393..c8c19f8 100644 --- a/src/components/ui/toast.tsx +++ b/app/src/components/ui/toast.tsx @@ -3,7 +3,7 @@ import * as ToastPrimitives from '@radix-ui/react-toast'; import { cva, type VariantProps } from 'class-variance-authority'; import { X } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const ToastProvider = ToastPrimitives.Provider; diff --git a/src/components/ui/toaster.tsx b/app/src/components/ui/toaster.tsx similarity index 100% rename from src/components/ui/toaster.tsx rename to app/src/components/ui/toaster.tsx diff --git a/src/components/ui/toggle-group.tsx b/app/src/components/ui/toggle-group.tsx similarity index 98% rename from src/components/ui/toggle-group.tsx rename to app/src/components/ui/toggle-group.tsx index a7ab2d8..2401f5f 100644 --- a/src/components/ui/toggle-group.tsx +++ b/app/src/components/ui/toggle-group.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group'; import { type VariantProps } from 'class-variance-authority'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' import { toggleVariants } from '@/components/ui/toggle-variants'; const ToggleGroupContext = React.createContext< diff --git a/src/components/ui/toggle-variants.ts b/app/src/components/ui/toggle-variants.ts similarity index 100% rename from src/components/ui/toggle-variants.ts rename to app/src/components/ui/toggle-variants.ts diff --git a/src/components/ui/toggle.tsx b/app/src/components/ui/toggle.tsx similarity index 95% rename from src/components/ui/toggle.tsx rename to app/src/components/ui/toggle.tsx index 44be4da..a18e30b 100644 --- a/src/components/ui/toggle.tsx +++ b/app/src/components/ui/toggle.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import * as TogglePrimitive from '@radix-ui/react-toggle'; import { type VariantProps } from 'class-variance-authority'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' import { toggleVariants } from './toggle-variants'; const Toggle = React.forwardRef< diff --git a/src/components/ui/tooltip.tsx b/app/src/components/ui/tooltip.tsx similarity index 97% rename from src/components/ui/tooltip.tsx rename to app/src/components/ui/tooltip.tsx index 33f3c17..3983bec 100644 --- a/src/components/ui/tooltip.tsx +++ b/app/src/components/ui/tooltip.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as TooltipPrimitive from '@radix-ui/react-tooltip'; -import { cn } from '@/lib/utils'; +import { cn } from '../../utils' const TooltipProvider = TooltipPrimitive.Provider; diff --git a/src/components/ui/use-toast.ts b/app/src/components/ui/use-toast.ts similarity index 100% rename from src/components/ui/use-toast.ts rename to app/src/components/ui/use-toast.ts diff --git a/src/components/ui/verification-step.tsx b/app/src/components/ui/verification-step.tsx similarity index 80% rename from src/components/ui/verification-step.tsx rename to app/src/components/ui/verification-step.tsx index 578f62e..3ef55ea 100644 --- a/src/components/ui/verification-step.tsx +++ b/app/src/components/ui/verification-step.tsx @@ -8,10 +8,9 @@ import { Loader2, AlertCircle, } from 'lucide-react'; -import { useAuth, useAuthActions } from '@/hooks'; -import { EVerificationStatus } from '@/types/identity'; -import { useAppKitAccount } from '@reown/appkit/react'; -import { OrdinalDetails, EnsDetails } from '@/types/identity'; +import { useAuth } from '@/hooks'; +import { EVerificationStatus } from '@opchan/core'; +import { OrdinalDetails, EnsDetails } from '@opchan/core'; interface VerificationStepProps { onComplete: () => void; @@ -26,20 +25,7 @@ export function VerificationStep({ isLoading, setIsLoading, }: VerificationStepProps) { - const { currentUser, verificationStatus, isAuthenticating } = useAuth(); - const { verifyWallet } = useAuthActions(); - - // Get account info to determine wallet type - const bitcoinAccount = useAppKitAccount({ namespace: 'bip122' }); - const ethereumAccount = useAppKitAccount({ namespace: 'eip155' }); - - const isBitcoinConnected = bitcoinAccount.isConnected; - const isEthereumConnected = ethereumAccount.isConnected; - const walletType = isBitcoinConnected - ? 'bitcoin' - : isEthereumConnected - ? 'ethereum' - : undefined; + const { currentUser, verifyOwnership } = useAuth(); const [verificationResult, setVerificationResult] = React.useState<{ success: boolean; @@ -53,24 +39,17 @@ export function VerificationStep({ verificationResult?.success && verificationResult.message.includes('Checking ownership') ) { - // Check if actual ownership was verified - // Treat centralized verification status as source of truth - const isOwnerVerified = - verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED; - const hasOwnership = - walletType === 'bitcoin' - ? isOwnerVerified && !!currentUser?.ordinalDetails - : isOwnerVerified && !!currentUser?.ensDetails; + const hasOwnership = currentUser?.verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED; if (hasOwnership) { setVerificationResult({ success: true, message: - walletType === 'bitcoin' + currentUser?.walletType === 'bitcoin' ? 'Ordinal ownership verified successfully!' : 'ENS ownership verified successfully!', details: - walletType === 'bitcoin' + currentUser?.walletType === 'bitcoin' ? currentUser?.ordinalDetails : currentUser?.ensDetails, }); @@ -78,49 +57,58 @@ export function VerificationStep({ setVerificationResult({ success: false, message: - walletType === 'bitcoin' + currentUser?.walletType === 'bitcoin' ? 'No Ordinal ownership found. You can still participate in the forum with your connected wallet!' : 'No ENS ownership found. You can still participate in the forum with your connected wallet!', }); } } - }, [currentUser, verificationResult, walletType, verificationStatus]); + }, [currentUser, verificationResult]); const handleVerify = async () => { - if (!currentUser) return; + console.log('🔘 Verify button clicked, currentUser:', currentUser); + if (!currentUser) { + console.log('❌ No currentUser in handleVerify'); + return; + } + console.log('🔄 Setting loading state and calling verifyWallet...'); setIsLoading(true); setVerificationResult(null); try { - const success = await verifyWallet(); - - if (success) { + console.log('📞 Calling verifyWallet()...'); + await verifyOwnership(); + if (currentUser?.verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED) { // For now, just show success - the actual ownership check will be done // by the useEffect when the user state updates + console.log('✅ Verification successful, setting result'); setVerificationResult({ success: true, message: - walletType === 'bitcoin' + currentUser?.walletType === 'bitcoin' ? 'Verification process completed. Checking ownership...' : 'Verification process completed. Checking ownership...', details: undefined, }); } else { + console.log('❌ Verification failed, setting failure result'); setVerificationResult({ success: false, message: - walletType === 'bitcoin' + currentUser?.walletType === 'bitcoin' ? 'No Ordinal ownership found. You can still participate in the forum with your connected wallet!' : 'No ENS ownership found. You can still participate in the forum with your connected wallet!', }); } } catch (error) { + console.error('💥 Error in handleVerify:', error); setVerificationResult({ success: false, message: `Verification failed. Please try again: ${error}`, }); } finally { + console.log('🔄 Setting loading to false'); setIsLoading(false); } }; @@ -130,19 +118,19 @@ export function VerificationStep({ }; const getVerificationType = () => { - return walletType === 'bitcoin' ? 'Bitcoin Ordinal' : 'Ethereum ENS'; + return currentUser?.walletType === 'bitcoin' ? 'Bitcoin Ordinal' : 'Ethereum ENS'; }; const getVerificationIcon = () => { - return walletType === 'bitcoin' ? Bitcoin : Coins; + return currentUser?.walletType === 'bitcoin' ? Bitcoin : Coins; }; const getVerificationColor = () => { - return walletType === 'bitcoin' ? 'text-orange-500' : 'text-blue-500'; + return currentUser?.walletType === 'bitcoin' ? 'text-orange-500' : 'text-blue-500'; }; const getVerificationDescription = () => { - if (walletType === 'bitcoin') { + if (currentUser?.walletType === 'bitcoin') { return "Verify your Bitcoin Ordinal ownership to unlock premium features. If you don't own any Ordinals, you can still participate in the forum with your connected wallet."; } else { return "Verify your Ethereum ENS ownership to unlock premium features. If you don't own any ENS, you can still participate in the forum with your connected wallet."; @@ -184,7 +172,7 @@ export function VerificationStep({

{verificationResult.details && (
- {walletType === 'bitcoin' ? ( + {currentUser?.walletType === 'bitcoin' ? (

Ordinal ID:{' '} {typeof verificationResult.details === 'object' && @@ -221,7 +209,7 @@ export function VerificationStep({ } // Show verification status - if (verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED) { + if (currentUser?.verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED) { return (

@@ -237,8 +225,8 @@ export function VerificationStep({

{currentUser && (
- {walletType === 'bitcoin' &&

Ordinal ID: Verified

} - {walletType === 'ethereum' &&

ENS Name: Verified

} + {currentUser?.walletType === 'bitcoin' &&

Ordinal ID: Verified

} + {currentUser?.walletType === 'ethereum' &&

ENS Name: Verified

}
)}
@@ -284,7 +272,7 @@ export function VerificationStep({
    - {walletType === 'bitcoin' ? ( + {currentUser?.walletType === 'bitcoin' ? ( <>
  • • We'll check your wallet for Bitcoin Ordinal ownership
  • • If found, you'll get full posting and voting access
  • @@ -309,10 +297,10 @@ export function VerificationStep({
    diff --git a/app/src/components/ui/waku-health-indicator.tsx b/app/src/components/ui/waku-health-indicator.tsx new file mode 100644 index 0000000..343a9cf --- /dev/null +++ b/app/src/components/ui/waku-health-indicator.tsx @@ -0,0 +1,78 @@ +import { Wifi, WifiOff, CheckCircle } from 'lucide-react'; +import { useNetwork } from '@opchan/react'; +import { cn } from '../../utils' + +interface WakuHealthIndicatorProps { + className?: string; + showText?: boolean; + size?: 'sm' | 'md' | 'lg'; +} + +export function WakuHealthIndicator({ + className, + showText = true, + size = 'md', +}: WakuHealthIndicatorProps) { + const {isConnected, statusMessage} = useNetwork(); + + const getIcon = () => { + if (isConnected === true) { + return ; + } else if (isConnected === false) { + return ; + } else { + return ; + } + }; + + const getSizeClasses = () => { + switch (size) { + case 'sm': + return 'w-4 h-4'; + case 'lg': + return 'w-6 h-6'; + default: + return 'w-5 h-5'; + } + }; + + return ( +
    +
    {getIcon()}
    + {showText && ( + + {statusMessage} + + )} +
    + ); +} + +/** + * Simple dot indicator for Waku health status + * Useful for compact displays like headers or status bars + */ +export function WakuHealthDot({ className }: { className?: string }) { + const { isConnected } = useNetwork(); + const statusColor = isConnected === true ? 'green' : isConnected === false ? 'red' : 'gray'; + + return ( +
    + ); +} diff --git a/src/components/ui/wallet-connection-step.tsx b/app/src/components/ui/wallet-connection-step.tsx similarity index 100% rename from src/components/ui/wallet-connection-step.tsx rename to app/src/components/ui/wallet-connection-step.tsx diff --git a/src/components/ui/wallet-dialog.tsx b/app/src/components/ui/wallet-dialog.tsx similarity index 98% rename from src/components/ui/wallet-dialog.tsx rename to app/src/components/ui/wallet-dialog.tsx index 4b8a1a0..6aa424c 100644 --- a/src/components/ui/wallet-dialog.tsx +++ b/app/src/components/ui/wallet-dialog.tsx @@ -194,7 +194,7 @@ export function WalletConnectionDialog({

    Address:

    - {activeAddress} + {activeAddress ? `${activeAddress.slice(0, 6)}...${activeAddress.slice(-4)}` : ''}

    diff --git a/src/components/ui/wallet-wizard.tsx b/app/src/components/ui/wallet-wizard.tsx similarity index 90% rename from src/components/ui/wallet-wizard.tsx rename to app/src/components/ui/wallet-wizard.tsx index 9c3f9a0..778cc17 100644 --- a/src/components/ui/wallet-wizard.tsx +++ b/app/src/components/ui/wallet-wizard.tsx @@ -9,8 +9,7 @@ import { import { Button } from '@/components/ui/button'; import { CheckCircle, Circle, Loader2 } from 'lucide-react'; import { useAuth } from '@/hooks'; -import { useDelegation } from '@/hooks/useDelegation'; -import { EVerificationStatus } from '@/types/identity'; +import { EVerificationStatus } from '@opchan/core'; import { WalletConnectionStep } from './wallet-connection-step'; import { VerificationStep } from './verification-step'; import { DelegationStep } from './delegation-step'; @@ -30,8 +29,8 @@ export function WalletWizard({ }: WalletWizardProps) { const [currentStep, setCurrentStep] = React.useState(1); const [isLoading, setIsLoading] = React.useState(false); - const { isAuthenticated, verificationStatus } = useAuth(); - const { delegationStatus } = useDelegation(); + const [delegationStatus, setDelegationStatus] = React.useState(false); + const { isAuthenticated, verificationStatus, delegationStatus: getDelegationStatus } = useAuth(); // Reset wizard when opened - always start at step 1 for simplicity React.useEffect(() => { @@ -41,6 +40,17 @@ export function WalletWizard({ } }, [open]); + // Load delegation status when component mounts or when user changes + React.useEffect(() => { + if (isAuthenticated) { + getDelegationStatus().then(status => { + setDelegationStatus(status.isValid); + }).catch(console.error); + } else { + setDelegationStatus(false); + } + }, [isAuthenticated, getDelegationStatus]); + const handleStepComplete = (step: WizardStep) => { if (step < 3) { setCurrentStep((step + 1) as WizardStep); @@ -65,7 +75,7 @@ export function WalletWizard({ case 2: return verificationStatus !== EVerificationStatus.WALLET_UNCONNECTED; case 3: - return delegationStatus.isValid; + return delegationStatus; default: return false; } diff --git a/app/src/hooks/index.ts b/app/src/hooks/index.ts new file mode 100644 index 0000000..57a0403 --- /dev/null +++ b/app/src/hooks/index.ts @@ -0,0 +1,11 @@ + +export { + useAuth , + useForum , + useNetwork, + usePermissions, + useContent, + useUIState, + useUserDisplay, +} from '@opchan/react'; + diff --git a/src/hooks/use-mobile.tsx b/app/src/hooks/use-mobile.tsx similarity index 100% rename from src/hooks/use-mobile.tsx rename to app/src/hooks/use-mobile.tsx diff --git a/src/hooks/use-toast.ts b/app/src/hooks/use-toast.ts similarity index 100% rename from src/hooks/use-toast.ts rename to app/src/hooks/use-toast.ts diff --git a/src/index.css b/app/src/index.css similarity index 100% rename from src/index.css rename to app/src/index.css diff --git a/app/src/main.tsx b/app/src/main.tsx new file mode 100644 index 0000000..ce6d4c9 --- /dev/null +++ b/app/src/main.tsx @@ -0,0 +1,22 @@ +import { createRoot } from 'react-dom/client'; +import App from './App.tsx'; +import './index.css'; +import { Buffer } from 'buffer'; +import { OpchanWithAppKit } from './providers/OpchanWithAppKit'; +import { WagmiProvider } from 'wagmi'; +import { AppKitProvider } from '@reown/appkit/react'; +import { appkitConfig, config } from '@opchan/core'; + +if (!(window as Window & typeof globalThis).Buffer) { + (window as Window & typeof globalThis).Buffer = Buffer; +} + +createRoot(document.getElementById('root')!).render( + + + + + + + +); diff --git a/src/pages/BookmarksPage.tsx b/app/src/pages/BookmarksPage.tsx similarity index 83% rename from src/pages/BookmarksPage.tsx rename to app/src/pages/BookmarksPage.tsx index e86da8d..48fabcd 100644 --- a/src/pages/BookmarksPage.tsx +++ b/app/src/pages/BookmarksPage.tsx @@ -16,27 +16,19 @@ import { AlertDialogTitle, AlertDialogTrigger, } from '@/components/ui/alert-dialog'; -import { useBookmarks } from '@/hooks'; -import { Bookmark, BookmarkType } from '@/types/forum'; +import { Bookmark, BookmarkType } from '@opchan/core'; import { Trash2, Bookmark as BookmarkIcon, FileText, MessageSquare, } from 'lucide-react'; -import { useAuth } from '@/contexts/useAuth'; +import { useAuth, useContent } from '@/hooks'; const BookmarksPage = () => { const { currentUser } = useAuth(); const navigate = useNavigate(); - const { - bookmarks, - loading, - error, - removeBookmark, - getBookmarksByType, - clearAllBookmarks, - } = useBookmarks(); + const { bookmarks, removeBookmark, clearAllBookmarks } = useContent(); const [activeTab, setActiveTab] = useState<'all' | 'posts' | 'comments'>( 'all' @@ -61,8 +53,8 @@ const BookmarksPage = () => { ); } - const postBookmarks = getBookmarksByType(BookmarkType.POST); - const commentBookmarks = getBookmarksByType(BookmarkType.COMMENT); + const postBookmarks = bookmarks.filter(bookmark => bookmark.type === BookmarkType.POST); + const commentBookmarks = bookmarks.filter(bookmark => bookmark.type === BookmarkType.COMMENT); const getFilteredBookmarks = () => { switch (activeTab) { @@ -87,36 +79,6 @@ const BookmarksPage = () => { await clearAllBookmarks(); }; - if (loading) { - return ( -
    -
    -
    -
    -
    -

    Loading bookmarks...

    -
    -
    -
    - ); - } - - if (error) { - return ( -
    -
    -
    -
    -

    - Error Loading Bookmarks -

    -

    {error}

    - -
    -
    -
    - ); - } return (
    diff --git a/src/pages/CellPage.tsx b/app/src/pages/CellPage.tsx similarity index 100% rename from src/pages/CellPage.tsx rename to app/src/pages/CellPage.tsx diff --git a/src/pages/Dashboard.tsx b/app/src/pages/Dashboard.tsx similarity index 100% rename from src/pages/Dashboard.tsx rename to app/src/pages/Dashboard.tsx diff --git a/src/pages/DebugPage.tsx b/app/src/pages/DebugPage.tsx similarity index 91% rename from src/pages/DebugPage.tsx rename to app/src/pages/DebugPage.tsx index d4483f1..be787a9 100644 --- a/src/pages/DebugPage.tsx +++ b/app/src/pages/DebugPage.tsx @@ -1,7 +1,6 @@ import { Fragment, useEffect, useMemo, useRef, useState } from 'react'; -import messageManager from '@/lib/waku'; -import { MessageType } from '@/types/waku'; -import type { OpchanMessage } from '@/types/forum'; +import { messageManager, MessageType } from '@opchan/core'; +import type { OpchanMessage } from '@opchan/core'; interface ReceivedMessage { receivedAt: number; @@ -14,11 +13,13 @@ export default function DebugPage() { useEffect(() => { // Subscribe to inbound messages from reliable channel - unsubscribeRef.current = messageManager.onMessageReceived(msg => { - setMessages(prev => - [{ receivedAt: Date.now(), message: msg }, ...prev].slice(0, 500) - ); - }); + unsubscribeRef.current = messageManager.onMessageReceived( + (msg: OpchanMessage) => { + setMessages(prev => + [{ receivedAt: Date.now(), message: msg }, ...prev].slice(0, 500) + ); + } + ); return () => { unsubscribeRef.current?.(); diff --git a/src/pages/FeedPage.tsx b/app/src/pages/FeedPage.tsx similarity index 85% rename from src/pages/FeedPage.tsx rename to app/src/pages/FeedPage.tsx index 14a3094..32357ce 100644 --- a/src/pages/FeedPage.tsx +++ b/app/src/pages/FeedPage.tsx @@ -12,36 +12,23 @@ import { import PostCard from '@/components/PostCard'; import FeedSidebar from '@/components/FeedSidebar'; import { ModerationToggle } from '@/components/ui/moderation-toggle'; -import { useForumData, useAuth, useForumActions } from '@/hooks'; -import { EVerificationStatus } from '@/types/identity'; -import { sortPosts, SortOption } from '@/lib/utils/sorting'; - +import { useAuth, useContent } from '@/hooks'; +import { EVerificationStatus } from '@opchan/core'; +import { sortPosts, SortOption } from '@/utils/sorting'; const FeedPage: React.FC = () => { - const forumData = useForumData(); + const content = useContent(); const { verificationStatus } = useAuth(); - const { refreshData } = useForumActions(); const [sortOption, setSortOption] = useState('relevance'); - const { - filteredPosts, - filteredCommentsByPost, - isInitialLoading, - isRefreshing, - } = forumData; - // ✅ Use pre-computed filtered data - const allPosts = useMemo(() => { - return sortPosts(filteredPosts, sortOption); - }, [filteredPosts, sortOption]); + // Build sorted posts from content slices + const allPosts = useMemo(() => sortPosts([...content.posts], sortOption), [content.posts, sortOption]); // ✅ Get comment count from filtered organized data - const getCommentCount = (postId: string) => { - const comments = filteredCommentsByPost[postId] || []; - return comments.length; - }; + const getCommentCount = (postId: string) => (content.commentsByPost[postId] || []).length; // Loading skeleton - if (isInitialLoading) { + if (!content.posts.length && !content.comments.length && !content.cells.length) { return (
    @@ -136,13 +123,11 @@ const FeedPage: React.FC = () => {
    @@ -166,7 +151,7 @@ const FeedPage: React.FC = () => { {verificationStatus !== EVerificationStatus.ENS_ORDINAL_VERIFIED && (

    - Connect your wallet and verify Ordinal ownership to + Connect your wallet to start posting

    )} diff --git a/src/pages/Index.tsx b/app/src/pages/Index.tsx similarity index 79% rename from src/pages/Index.tsx rename to app/src/pages/Index.tsx index 40e81ca..6975661 100644 --- a/src/pages/Index.tsx +++ b/app/src/pages/Index.tsx @@ -1,22 +1,21 @@ import Header from '@/components/Header'; import CellList from '@/components/CellList'; -import { useNetworkStatus, useForumActions } from '@/hooks'; import { Button } from '@/components/ui/button'; import { Wifi } from 'lucide-react'; +import { useForum } from '@/hooks'; const Index = () => { - const { health } = useNetworkStatus(); - const { refreshData } = useForumActions(); + const { network, content } = useForum(); return (
    - {!health.isConnected && ( + {!network.isConnected && (
    - {userInfo.displayName} + {currentUser.displayName}
    - {userInfo.ordinalDetails || currentUser.ordinalDetails?.ordinalDetails - ? `Ordinal: ${userInfo.ordinalDetails || currentUser.ordinalDetails?.ordinalDetails}` - : currentUser.ensDetails?.ensName || 'No ENS name'} -
    + {/* Show ENS name if available */} + {(currentUser.ensDetails?.ensName ) && ( +
    + ENS:{' '} + {currentUser.ensDetails?.ensName} +
    + )} + {/* Show Ordinal details if available */} + {(currentUser.ordinalDetails ) && ( +
    + Ordinal:{' '} + {currentUser.ordinalDetails.ordinalDetails} +
    + )} + {/* Show fallback if neither ENS nor Ordinal */} + {!( + currentUser.ensDetails?.ensName + ) && + !( + currentUser.ordinalDetails?.ordinalDetails + ) &&
    No ENS or Ordinal verification
    }
    {getVerificationIcon()} @@ -290,6 +282,7 @@ export default function ProfilePage() {
    +
    {/* Wallet Section */}
    @@ -359,7 +352,7 @@ export default function ProfilePage() { /> ) : (
    - {userInfo.callSign || + {currentUser.callSign || currentUser.callSign || 'Not set'}
    @@ -405,7 +398,7 @@ export default function ProfilePage() { ) : (
    - {(userInfo.displayPreference || + {(currentUser.displayPreference || displayPreference) === EDisplayPreference.CALL_SIGN ? 'Call Sign (when available)' @@ -455,8 +448,7 @@ export default function ProfilePage() { Security
    - {(delegationStatus.hasDelegation || - delegationInfo?.hasDelegation) && ( + {delegationInfo.hasDelegation && ( )} @@ -479,35 +469,25 @@ export default function ProfilePage() { Delegation - {delegationStatus.isValid || delegationInfo?.isValid - ? 'Active' - : 'Inactive'} + {delegationInfo.isValid ? 'Active' : 'Inactive'}
    {/* Expiry Date */} - {(delegationStatus.expiresAt || - currentUser.delegationExpiry) && ( + {delegationInfo.expiresAt && (
    Valid until
    - {( - delegationStatus.expiresAt || - new Date(currentUser.delegationExpiry!) - ).toLocaleDateString()} + {delegationInfo.expiresAt.toLocaleDateString()}
    )} @@ -520,16 +500,12 @@ export default function ProfilePage() { - {delegationStatus.isValid || - currentUser.delegationSignature === 'valid' - ? 'Valid' - : 'Not signed'} + {delegationInfo.isValid ? 'Valid' : 'Not signed'}
    @@ -541,19 +517,17 @@ export default function ProfilePage() {
    - {delegationStatus.publicKey || currentUser.browserPubKey - ? `${(delegationStatus.publicKey || currentUser.browserPubKey!).slice(0, 12)}...${(delegationStatus.publicKey || currentUser.browserPubKey!).slice(-8)}` + {delegationInfo.publicKey + ? `${delegationInfo.publicKey.slice(0, 12)}...${delegationInfo.publicKey.slice(-8)}` : 'Not delegated'}
    - {(delegationStatus.publicKey || - currentUser.browserPubKey) && ( + {delegationInfo.publicKey && (
    {/* Warning for expired delegation */} - {(!delegationStatus.isValid && - delegationStatus.hasDelegation) || - (!delegationInfo?.isValid && - delegationInfo?.hasDelegation && ( + {(!delegationInfo.isValid && delegationInfo.hasDelegation) && (
    @@ -579,7 +550,7 @@ export default function ProfilePage() {
    - ))} + )}
diff --git a/app/src/providers/OpchanWithAppKit.tsx b/app/src/providers/OpchanWithAppKit.tsx new file mode 100644 index 0000000..2ffa63e --- /dev/null +++ b/app/src/providers/OpchanWithAppKit.tsx @@ -0,0 +1,60 @@ +import * as React from 'react'; +import { OpChanProvider, type WalletAdapter, type WalletAdapterAccount } from '@opchan/react'; +import { useAppKitAccount, modal } from '@reown/appkit/react'; +import { AppKit } from '@reown/appkit'; +import type { OpChanClientConfig } from '@opchan/core'; +import { walletManager } from '@opchan/core'; + +interface Props { config: OpChanClientConfig; children: React.ReactNode } + +export const OpchanWithAppKit: React.FC = ({ config, children }) => { + const btc = useAppKitAccount({ namespace: 'bip122' }); + const eth = useAppKitAccount({ namespace: 'eip155' }); + + const listenersRef = React.useRef(new Set<(a: WalletAdapterAccount | null) => void>()); + + const getCurrent = React.useCallback((): WalletAdapterAccount | null => { + if (btc.isConnected && btc.address) return { address: btc.address, walletType: 'bitcoin' }; + if (eth.isConnected && eth.address) return { address: eth.address, walletType: 'ethereum' }; + return null; + }, [btc.isConnected, btc.address, eth.isConnected, eth.address]); + + const adapter = React.useMemo(() => ({ + getAccount: () => getCurrent(), + onChange: (cb) => { + listenersRef.current.add(cb); + return () => { listenersRef.current.delete(cb); }; + }, + }), [getCurrent]); + + // Notify listeners when AppKit account changes + React.useEffect(() => { + const account = getCurrent(); + listenersRef.current.forEach(cb => { + try { cb(account); } catch { /* ignore */ } + }); + }, [getCurrent]); + + React.useEffect(() => { + if (!modal) return; + try { + const hasBtc = btc.isConnected && !!btc.address; + const hasEth = eth.isConnected && !!eth.address; + if (hasBtc || hasEth) { + walletManager.create(modal as AppKit, btc, eth); + } else if (walletManager.hasInstance()) { + walletManager.clear(); + } + } catch (err) { + console.warn('WalletManager initialization error', err); + } + }, [btc, btc.isConnected, btc.address, eth, eth.isConnected, eth.address]); + + return ( + + {children} + + ); +}; + + diff --git a/src/test/setup.ts b/app/src/test/setup.ts similarity index 100% rename from src/test/setup.ts rename to app/src/test/setup.ts diff --git a/app/src/utils/index.ts b/app/src/utils/index.ts new file mode 100644 index 0000000..eedf536 --- /dev/null +++ b/app/src/utils/index.ts @@ -0,0 +1,9 @@ +import { clsx, type ClassValue } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); + } + + export { urlLoads } from './urlLoads'; \ No newline at end of file diff --git a/src/lib/utils/sorting.ts b/app/src/utils/sorting.ts similarity index 95% rename from src/lib/utils/sorting.ts rename to app/src/utils/sorting.ts index ba95355..9ed2676 100644 --- a/src/lib/utils/sorting.ts +++ b/app/src/utils/sorting.ts @@ -1,4 +1,4 @@ -import { Post, Comment, Cell } from '@/types/forum'; +import { Post, Comment, Cell } from '../../../packages/core/src/types/forum'; export type SortOption = 'relevance' | 'time'; diff --git a/src/lib/utils/urlLoads.test.ts b/app/src/utils/urlLoads.test.ts similarity index 100% rename from src/lib/utils/urlLoads.test.ts rename to app/src/utils/urlLoads.test.ts diff --git a/src/lib/utils/urlLoads.ts b/app/src/utils/urlLoads.ts similarity index 100% rename from src/lib/utils/urlLoads.ts rename to app/src/utils/urlLoads.ts diff --git a/src/vite-env.d.ts b/app/src/vite-env.d.ts similarity index 100% rename from src/vite-env.d.ts rename to app/src/vite-env.d.ts diff --git a/tailwind.config.ts b/app/tailwind.config.ts similarity index 100% rename from tailwind.config.ts rename to app/tailwind.config.ts diff --git a/tsconfig.app.json b/app/tsconfig.app.json similarity index 100% rename from tsconfig.app.json rename to app/tsconfig.app.json diff --git a/tsconfig.app.tsbuildinfo b/app/tsconfig.app.tsbuildinfo similarity index 100% rename from tsconfig.app.tsbuildinfo rename to app/tsconfig.app.tsbuildinfo diff --git a/app/tsconfig.json b/app/tsconfig.json new file mode 100644 index 0000000..0286778 --- /dev/null +++ b/app/tsconfig.json @@ -0,0 +1,35 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ], + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + }, + /* Enhanced Type Checking */ + "strict": true, + "noImplicitAny": true, + "noUnusedParameters": true, + "noUnusedLocals": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "allowUnusedLabels": false, + "allowUnreachableCode": false, + + /* Module Resolution */ + "skipLibCheck": true, + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true + } +} diff --git a/tsconfig.node.json b/app/tsconfig.node.json similarity index 100% rename from tsconfig.node.json rename to app/tsconfig.node.json diff --git a/tsconfig.tsbuildinfo b/app/tsconfig.tsbuildinfo similarity index 100% rename from tsconfig.tsbuildinfo rename to app/tsconfig.tsbuildinfo diff --git a/vite.config.ts b/app/vite.config.ts similarity index 87% rename from vite.config.ts rename to app/vite.config.ts index 31f8c5d..66c3ccb 100644 --- a/vite.config.ts +++ b/app/vite.config.ts @@ -16,7 +16,7 @@ export default defineConfig(() => ({ }, }, optimizeDeps: { - include: ['buffer'], + include: ['buffer', '@opchan/core', '@opchan/hooks'], }, build: { target: 'es2022', diff --git a/vitest.config.ts b/app/vitest.config.ts similarity index 100% rename from vitest.config.ts rename to app/vitest.config.ts diff --git a/package-lock.json b/package-lock.json index cb75616..c40fc28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,37 @@ { "name": "opchan", - "version": "0.1.0", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "opchan", - "version": "0.1.0", + "version": "1.0.0", + "workspaces": [ + "packages/*", + "app" + ], + "devDependencies": { + "@types/node": "^20.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "eslint": "^8.0.0", + "tsx": "^4.0.0", + "typescript": "^5.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "app": { + "name": "opchan", + "version": "1.0.0", "dependencies": { "@hookform/resolvers": "^3.9.0", "@noble/ed25519": "^2.2.3", "@noble/hashes": "^1.8.0", + "@opchan/core": "file:../packages/core", + "@opchan/react": "file:../packages/react", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-aspect-ratio": "^1.1.0", @@ -43,7 +64,6 @@ "@reown/appkit-adapter-wagmi": "^1.7.17", "@reown/appkit-wallet-button": "^1.7.17", "@tanstack/react-query": "^5.84.1", - "@waku/sdk": "^0.0.35-67a7287.0", "buffer": "^6.0.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -53,7 +73,6 @@ "input-otp": "^1.2.4", "lucide-react": "^0.462.0", "next-themes": "^0.3.0", - "ordiscan": "^1.3.0", "re-resizable": "6.11.2", "react": "^18.3.1", "react-day-picker": "^8.10.1", @@ -70,8 +89,8 @@ "tailwindcss-animate": "^1.0.7", "uuid": "^11.1.0", "vaul": "^0.9.3", - "viem": "^2.37.1", - "wagmi": "^2.16.1", + "viem": "^2.37.6", + "wagmi": "^2.17.0", "zod": "^3.23.8" }, "devDependencies": { @@ -84,7 +103,7 @@ "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@types/uuid": "^10.0.0", - "@vitejs/plugin-react-swc": "^3.5.0", + "@vitejs/plugin-react-swc": "^3.11.0", "@vitest/ui": "^3.2.4", "autoprefixer": "^10.4.20", "eslint": "^9.9.0", @@ -101,10 +120,81 @@ "vitest": "^3.2.4" } }, + "app/node_modules/@types/node": { + "version": "22.18.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.6.tgz", + "integrity": "sha512-r8uszLPpeIWbNKtvWRt/DbVi5zbqZyj1PTmhRMqBMvDnaz1QpmSKujUtJLrqGZeoM8v72MfYggDceY4K1itzWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "app/node_modules/eslint": { + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", + "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.35.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, "node_modules/@adobe/css-tools": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.3.tgz", - "integrity": "sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", "dev": true, "license": "MIT" }, @@ -168,9 +258,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", - "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -213,22 +303,6 @@ "node": ">=6" } }, - "node_modules/@base-org/account/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, - "node_modules/@bitcoinerlab/secp256k1": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@bitcoinerlab/secp256k1/-/secp256k1-1.2.0.tgz", - "integrity": "sha512-jeujZSzb3JOZfmJYI0ph1PVpCRV5oaexCgy+RvCXV8XlY+XFB/2n3WOcvBsKLsOw78KYgnQrQWb2HrKE4be88Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "@noble/curves": "^1.7.0" - } - }, "node_modules/@chainsafe/as-chacha20poly1305": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@chainsafe/as-chacha20poly1305/-/as-chacha20poly1305-0.1.0.tgz", @@ -318,16 +392,10 @@ "node": ">=6" } }, - "node_modules/@coinbase/wallet-sdk/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, "node_modules/@csstools/color-helpers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", - "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", "dev": true, "funding": [ { @@ -369,9 +437,9 @@ } }, "node_modules/@csstools/css-color-parser": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", - "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", "dev": true, "funding": [ { @@ -385,7 +453,7 @@ ], "license": "MIT", "dependencies": { - "@csstools/color-helpers": "^5.0.2", + "@csstools/color-helpers": "^5.1.0", "@csstools/css-calc": "^2.1.4" }, "engines": { @@ -439,10 +507,24 @@ "node": ">=18" } }, + "node_modules/@ecies/ciphers": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.4.tgz", + "integrity": "sha512-t+iX+Wf5nRKyNzk8dviW3Ikb/280+aEJAnw9YXvCp2tYGPSkMki+NRY+8aNLmVFv3eNtMdvViPNOPxS8SZNP+w==", + "license": "MIT", + "engines": { + "bun": ">=1", + "deno": ">=2", + "node": ">=16" + }, + "peerDependencies": { + "@noble/ciphers": "^1.0.0" + } + }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", "cpu": [ "ppc64" ], @@ -453,13 +535,13 @@ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", "cpu": [ "arm" ], @@ -470,13 +552,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", "cpu": [ "arm64" ], @@ -487,13 +569,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", "cpu": [ "x64" ], @@ -504,13 +586,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", "cpu": [ "arm64" ], @@ -521,13 +603,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", "cpu": [ "x64" ], @@ -538,13 +620,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", "cpu": [ "arm64" ], @@ -555,13 +637,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", "cpu": [ "x64" ], @@ -572,13 +654,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", "cpu": [ "arm" ], @@ -589,13 +671,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", "cpu": [ "arm64" ], @@ -606,13 +688,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", "cpu": [ "ia32" ], @@ -623,13 +705,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", "cpu": [ "loong64" ], @@ -640,13 +722,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", "cpu": [ "mips64el" ], @@ -657,13 +739,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", "cpu": [ "ppc64" ], @@ -674,13 +756,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", "cpu": [ "riscv64" ], @@ -691,13 +773,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", "cpu": [ "s390x" ], @@ -708,13 +790,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", "cpu": [ "x64" ], @@ -725,13 +807,30 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", "cpu": [ "x64" ], @@ -742,13 +841,30 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", "cpu": [ "x64" ], @@ -759,13 +875,30 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", "cpu": [ "x64" ], @@ -776,13 +909,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", "cpu": [ "arm64" ], @@ -793,13 +926,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", "cpu": [ "ia32" ], @@ -810,13 +943,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", "cpu": [ "x64" ], @@ -827,21 +960,24 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } @@ -860,9 +996,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "license": "MIT", "engines": { @@ -870,13 +1006,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", - "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -884,20 +1020,33 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/core": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", - "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "license": "MIT", "dependencies": { @@ -932,19 +1081,22 @@ } }, "node_modules/@eslint/js": { - "version": "9.13.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz", - "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", + "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -952,11 +1104,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", - "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, + "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -1120,31 +1274,31 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", - "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.2.8" + "@floating-ui/utils": "^0.2.10" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.11", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.11.tgz", - "integrity": "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.8" + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", - "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.0.0" + "@floating-ui/dom": "^1.7.4" }, "peerDependencies": { "react": ">=16.8.0", @@ -1152,24 +1306,37 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", - "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==", + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", "license": "MIT" }, + "node_modules/@gemini-wallet/core": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@gemini-wallet/core/-/core-0.2.0.tgz", + "integrity": "sha512-vv9aozWnKrrPWQ3vIFcWk7yta4hQW1Ie0fsNNPeXnjAxkbXr2hqMagEptLuMxpEP2W3mnRu05VDNKzcvAuuZDw==", + "license": "MIT", + "dependencies": { + "@metamask/rpc-errors": "7.0.2", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "viem": ">=2.0.0" + } + }, "node_modules/@hookform/resolvers": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.0.tgz", - "integrity": "sha512-bU0Gr4EepJ/EQsH/IwEzYLsT/PEj5C0ynLQ4m+GSHS+xKH4TfSelhluTgOaoc4kA5s7eCsQbM4wvZLzELmWzUg==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", + "integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==", "license": "MIT", "peerDependencies": { "react-hook-form": "^7.0.0" } }, "node_modules/@humanfs/core": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", - "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1177,19 +1344,35 @@ } }, "node_modules/@humanfs/node": { - "version": "0.16.5", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", - "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==", + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.0", - "@humanwhocodes/retry": "^0.3.0" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1204,10 +1387,18 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1235,18 +1426,41 @@ "node": ">=12" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=6.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, "node_modules/@jridgewell/resolve-uri": { @@ -1258,25 +1472,16 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1618,6 +1823,39 @@ "node": ">=16.0.0" } }, + "node_modules/@metamask/eth-json-rpc-provider/node_modules/@metamask/rpc-errors": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@metamask/rpc-errors/-/rpc-errors-6.4.0.tgz", + "integrity": "sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==", + "license": "MIT", + "dependencies": { + "@metamask/utils": "^9.0.0", + "fast-safe-stringify": "^2.0.6" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/eth-json-rpc-provider/node_modules/@metamask/rpc-errors/node_modules/@metamask/utils": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-9.3.0.tgz", + "integrity": "sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.1.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@metamask/eth-json-rpc-provider/node_modules/@metamask/utils": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-5.0.2.tgz", @@ -1661,6 +1899,72 @@ "node": ">=16.0.0" } }, + "node_modules/@metamask/json-rpc-engine/node_modules/@metamask/rpc-errors": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@metamask/rpc-errors/-/rpc-errors-6.4.0.tgz", + "integrity": "sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==", + "license": "MIT", + "dependencies": { + "@metamask/utils": "^9.0.0", + "fast-safe-stringify": "^2.0.6" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/json-rpc-engine/node_modules/@metamask/rpc-errors/node_modules/@metamask/utils": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-9.3.0.tgz", + "integrity": "sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.1.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/json-rpc-engine/node_modules/@metamask/utils": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.5.0.tgz", + "integrity": "sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.0.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/json-rpc-engine/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@metamask/json-rpc-middleware-stream": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/@metamask/json-rpc-middleware-stream/-/json-rpc-middleware-stream-7.0.2.tgz", @@ -1676,6 +1980,39 @@ "node": ">=16.0.0" } }, + "node_modules/@metamask/json-rpc-middleware-stream/node_modules/@metamask/utils": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.5.0.tgz", + "integrity": "sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.0.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/json-rpc-middleware-stream/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@metamask/object-multiplex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@metamask/object-multiplex/-/object-multiplex-2.1.0.tgz", @@ -1721,7 +2058,7 @@ "node": "^18.18 || >=20" } }, - "node_modules/@metamask/rpc-errors": { + "node_modules/@metamask/providers/node_modules/@metamask/rpc-errors": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/@metamask/rpc-errors/-/rpc-errors-6.4.0.tgz", "integrity": "sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==", @@ -1734,7 +2071,7 @@ "node": ">=16.0.0" } }, - "node_modules/@metamask/rpc-errors/node_modules/@metamask/utils": { + "node_modules/@metamask/providers/node_modules/@metamask/rpc-errors/node_modules/@metamask/utils": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-9.3.0.tgz", "integrity": "sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==", @@ -1754,7 +2091,27 @@ "node": ">=16.0.0" } }, - "node_modules/@metamask/rpc-errors/node_modules/uuid": { + "node_modules/@metamask/providers/node_modules/@metamask/utils": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.5.0.tgz", + "integrity": "sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.0.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/providers/node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", @@ -1767,6 +2124,19 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/@metamask/rpc-errors": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@metamask/rpc-errors/-/rpc-errors-7.0.2.tgz", + "integrity": "sha512-YYYHsVYd46XwY2QZzpGeU4PSdRhHdxnzkB8piWGvJW2xbikZ3R+epAYEL4q/K8bh9JPTucsUdwRFnACor1aOYw==", + "license": "MIT", + "dependencies": { + "@metamask/utils": "^11.0.1", + "fast-safe-stringify": "^2.0.6" + }, + "engines": { + "node": "^18.20 || ^20.17 || >=22" + } + }, "node_modules/@metamask/safe-event-emitter": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-3.1.2.tgz", @@ -1873,23 +2243,25 @@ } }, "node_modules/@metamask/utils": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.5.0.tgz", - "integrity": "sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==", + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-11.8.0.tgz", + "integrity": "sha512-EJqiuvVBAjV1vd1kBhmVmRtGfadrBfY3ImcAMjl+8MSSByTB3VNwvlIBLQdp+TwdAomUdenJCx2BvOSQykm8Hg==", "license": "ISC", "dependencies": { "@ethereumjs/tx": "^4.2.0", - "@metamask/superstruct": "^3.0.0", + "@metamask/superstruct": "^3.1.0", "@noble/hashes": "^1.3.1", "@scure/base": "^1.1.3", "@types/debug": "^4.1.7", + "@types/lodash": "^4.17.20", "debug": "^4.3.4", + "lodash": "^4.17.21", "pony-cause": "^2.1.10", "semver": "^7.5.4", "uuid": "^9.0.1" }, "engines": { - "node": ">=16.0.0" + "node": "^18.18 || ^20.14 || >=22" } }, "node_modules/@metamask/utils/node_modules/uuid": { @@ -1915,12 +2287,11 @@ } }, "node_modules/@multiformats/dns": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@multiformats/dns/-/dns-1.0.6.tgz", - "integrity": "sha512-nt/5UqjMPtyvkG9BQYdJ4GfLK3nMqGpFZOzf4hAmIa0sJh2LlS9YKXZ4FgwBDsaHvzZqR/rUFIywIc7pkHNNuw==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@multiformats/dns/-/dns-1.0.9.tgz", + "integrity": "sha512-Ja4hevWI9p96ICx11K3suFvFirnMmXILzS7FpsR2KG3FoKF/XJijm8ylf3vY6kRFGr98yfZYM+zIn18KaINs3A==", "license": "Apache-2.0 OR MIT", "dependencies": { - "@types/dns-packet": "^5.6.5", "buffer": "^6.0.3", "dns-packet": "^5.6.1", "hashlru": "^2.3.0", @@ -1986,9 +2357,9 @@ } }, "node_modules/@noble/curves": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.2.tgz", - "integrity": "sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==", + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", "license": "MIT", "dependencies": { "@noble/hashes": "1.8.0" @@ -2001,9 +2372,9 @@ } }, "node_modules/@noble/ed25519": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-2.2.3.tgz", - "integrity": "sha512-iHV8eI2mRcUmOx159QNrU8vTpQ/Xm70yJ2cTk3Trc86++02usfqFoNl6x0p3JN81ZDS/1gx6xiK0OwrgqCT43g==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-2.3.0.tgz", + "integrity": "sha512-M7dvXL2B92/M7dw9+gzuydL8qn/jiqNHaoR3Q+cb1q1GHV7uwE17WCyFMG+Y+TZb5izcaXk5TdJRrDUxHXL78A==", "license": "MIT", "funding": { "url": "https://paulmillr.com/funding/" @@ -2068,6 +2439,14 @@ "node": ">= 8" } }, + "node_modules/@opchan/core": { + "resolved": "packages/core", + "link": true + }, + "node_modules/@opchan/react": { + "resolved": "packages/react", + "link": true + }, "node_modules/@paulmillr/qr": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@paulmillr/qr/-/qr-0.2.1.tgz", @@ -2078,6 +2457,15 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@phosphor-icons/webcomponents": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@phosphor-icons/webcomponents/-/webcomponents-2.1.5.tgz", + "integrity": "sha512-JcvQkZxvcX2jK+QCclm8+e8HXqtdFW9xV4/kk2aL9Y3dJA2oQVt+pzbv1orkumz3rfx4K9mn9fDoMr1He1yr7Q==", + "license": "MIT", + "dependencies": { + "lit": "^3" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2096,32 +2484,32 @@ "license": "MIT" }, "node_modules/@radix-ui/number": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", - "integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", "license": "MIT" }, "node_modules/@radix-ui/primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", - "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", "license": "MIT" }, "node_modules/@radix-ui/react-accordion": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.1.tgz", - "integrity": "sha512-bg/l7l5QzUjgsh8kjwDFommzAshnUsuVMV5NM56QVCm+7ZckYdd9P/ExR8xG/Oup0OajVxNLaHJ1tb8mXk+nzQ==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.12.tgz", + "integrity": "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-collapsible": "1.1.1", - "@radix-ui/react-collection": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collapsible": "1.1.12", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -2139,17 +2527,17 @@ } }, "node_modules/@radix-ui/react-alert-dialog": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.2.tgz", - "integrity": "sha512-eGSlLzPhKO+TErxkiGcCZGuvbVMnLA1MTnyBksGOeGRGkxHiiJUujsjmNTdWTm4iHVSRaUao9/4Ur671auMghQ==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", + "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dialog": "1.1.2", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dialog": "1.1.15", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2167,12 +2555,12 @@ } }, "node_modules/@radix-ui/react-arrow": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", - "integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2190,12 +2578,12 @@ } }, "node_modules/@radix-ui/react-aspect-ratio": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.0.tgz", - "integrity": "sha512-dP87DM/Y7jFlPgUZTlhx6FF5CEzOiaxp2rBCKlaXlpH5Ip/9Fg5zZ9lDOQ5o/MOfUlf36eak14zoWYpgcgGoOg==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.7.tgz", + "integrity": "sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2213,15 +2601,16 @@ } }, "node_modules/@radix-ui/react-avatar": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.1.tgz", - "integrity": "sha512-eoOtThOmxeoizxpX6RiEsQZ2wj5r4+zoeqAwO0cBaFQGjJwIH3dIX0OCxNrCyrrdxG+vBweMETh3VziQG7c1kw==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz", + "integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==", "license": "MIT", "dependencies": { - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-is-hydrated": "0.1.0", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2239,19 +2628,19 @@ } }, "node_modules/@radix-ui/react-checkbox": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.2.tgz", - "integrity": "sha512-/i0fl686zaJbDQLNKrkCbMyDm6FQMt4jg323k7HuqitoANm9sE23Ql8yOK3Wusk34HSLKDChhMux05FnP6KUkw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", + "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-previous": "1.1.0", - "@radix-ui/react-use-size": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2269,19 +2658,19 @@ } }, "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.1.tgz", - "integrity": "sha512-1///SnrfQHJEofLokyczERxQbWfCGQlQ2XsCZMucVs6it+lq9iw4vXy+uDn1edlb58cOZOWSldnfPAYcT4O/Yg==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", + "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2299,15 +2688,15 @@ } }, "node_modules/@radix-ui/react-collection": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", - "integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2324,25 +2713,10 @@ } } }, - "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-context": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", - "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", - "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2355,9 +2729,9 @@ } }, "node_modules/@radix-ui/react-context": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", - "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2370,17 +2744,17 @@ } }, "node_modules/@radix-ui/react-context-menu": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.2.2.tgz", - "integrity": "sha512-99EatSTpW+hRYHt7m8wdDlLtkmTovEe8Z/hnxUPV+SKuuNL5HWNhQI4QSdjZqNSgXHay2z4M3Dym73j9p2Gx5Q==", + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.2.16.tgz", + "integrity": "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-menu": "2.1.2", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -2398,25 +2772,25 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.2.tgz", - "integrity": "sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.1", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-portal": "1.1.2", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.6.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", @@ -2434,9 +2808,9 @@ } }, "node_modules/@radix-ui/react-direction": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", - "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2449,16 +2823,16 @@ } }, "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz", - "integrity": "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2476,18 +2850,18 @@ } }, "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.2.tgz", - "integrity": "sha512-GVZMR+eqK8/Kes0a36Qrv+i20bAPXSn8rCBTHx30w+3ECnR5o3xixAlqcVaYvLeyKUsm0aqyhWfmUcqufM8nYA==", + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-menu": "2.1.2", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -2505,9 +2879,9 @@ } }, "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", - "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2520,14 +2894,14 @@ } }, "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz", - "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2545,20 +2919,20 @@ } }, "node_modules/@radix-ui/react-hover-card": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.2.tgz", - "integrity": "sha512-Y5w0qGhysvmqsIy6nQxaPa6mXNKznfoGjOfBgzOjocLxr2XlSjqBMYQQL+FfyogsMuX+m8cZyQGYhJxvxUzO4w==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.15.tgz", + "integrity": "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.1", - "@radix-ui/react-popper": "1.2.0", - "@radix-ui/react-portal": "1.1.2", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -2576,12 +2950,12 @@ } }, "node_modules/@radix-ui/react-id": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", - "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2594,12 +2968,12 @@ } }, "node_modules/@radix-ui/react-label": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.0.tgz", - "integrity": "sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2617,29 +2991,29 @@ } }, "node_modules/@radix-ui/react-menu": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.2.tgz", - "integrity": "sha512-lZ0R4qR2Al6fZ4yCCZzu/ReTFrylHFxIqy7OezIpWF4bL0o9biKo0pFIvkaew3TyZ9Fy5gYVrR5zCGZBVbO1zg==", + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-collection": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-dismissable-layer": "1.1.1", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.0", - "@radix-ui/react-portal": "1.1.2", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-roving-focus": "1.1.0", - "@radix-ui/react-slot": "1.1.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.6.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", @@ -2657,21 +3031,21 @@ } }, "node_modules/@radix-ui/react-menubar": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.2.tgz", - "integrity": "sha512-cKmj5Gte7LVyuz+8gXinxZAZECQU+N7aq5pw7kUPpx3xjnDXDbsdzHtCCD2W72bwzy74AvrqdYnKYS42ueskUQ==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.16.tgz", + "integrity": "sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-collection": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-menu": "2.1.2", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-roving-focus": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -2689,25 +3063,25 @@ } }, "node_modules/@radix-ui/react-navigation-menu": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.1.tgz", - "integrity": "sha512-egDo0yJD2IK8L17gC82vptkvW1jLeni1VuqCyzY727dSJdk5cDjINomouLoNk8RVF7g2aNIfENKWL4UzeU9c8Q==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.14.tgz", + "integrity": "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-collection": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-dismissable-layer": "1.1.1", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-previous": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2725,26 +3099,26 @@ } }, "node_modules/@radix-ui/react-popover": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.2.tgz", - "integrity": "sha512-u2HRUyWW+lOiA2g0Le0tMmT55FGOEWHwPFt1EPfbLly7uXQExFo5duNKqG2DzmFXIdqOeNd+TpE8baHWJCyP9w==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", + "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.1", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.0", - "@radix-ui/react-portal": "1.1.2", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.6.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", @@ -2762,21 +3136,21 @@ } }, "node_modules/@radix-ui/react-popper": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", - "integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-rect": "1.1.0", - "@radix-ui/react-use-size": "1.1.0", - "@radix-ui/rect": "1.1.0" + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2793,29 +3167,14 @@ } } }, - "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-context": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", - "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-portal": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz", - "integrity": "sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2833,13 +3192,13 @@ } }, "node_modules/@radix-ui/react-presence": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz", - "integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2857,12 +3216,12 @@ } }, "node_modules/@radix-ui/react-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", - "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.1.0" + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2880,13 +3239,13 @@ } }, "node_modules/@radix-ui/react-progress": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.0.tgz", - "integrity": "sha512-aSzvnYpP725CROcxAOEBVZZSIQVQdHgBr2QQFKySsaD14u8dNT0batuXI+AAGDdAHfXH8rbnHmjYFqVJ21KkRg==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz", + "integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==", "license": "MIT", "dependencies": { - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-primitive": "2.0.0" + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2903,37 +3262,22 @@ } } }, - "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-context": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", - "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-radio-group": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.2.1.tgz", - "integrity": "sha512-kdbv54g4vfRjja9DNWPMxKvXblzqbpEC8kspEkZ6dVP7kQksGCn+iZHkcCz2nb00+lPdRvxrqy4WrvvV1cNqrQ==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz", + "integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-roving-focus": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-previous": "1.1.0", - "@radix-ui/react-use-size": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2951,20 +3295,20 @@ } }, "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz", - "integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-collection": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -2981,36 +3325,21 @@ } } }, - "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-context": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", - "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.0.tgz", - "integrity": "sha512-q2jMBdsJ9zB7QG6ngQNzNwlvxLQqONyL58QbEGwuyRZZb/ARQwk3uQVbCF7GvQVOtV6EU/pDxAw3zRzJZI3rpQ==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz", + "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==", "license": "MIT", "dependencies": { - "@radix-ui/number": "1.1.0", - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -3028,32 +3357,32 @@ } }, "node_modules/@radix-ui/react-select": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.2.tgz", - "integrity": "sha512-rZJtWmorC7dFRi0owDmoijm6nSJH1tVw64QGiNIZ9PNLyBDtG+iAq+XGsya052At4BfarzY/Dhv9wrrUr6IMZA==", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", "license": "MIT", "dependencies": { - "@radix-ui/number": "1.1.0", - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-collection": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-dismissable-layer": "1.1.1", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.0", - "@radix-ui/react-portal": "1.1.2", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-previous": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.6.0" + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", @@ -3071,12 +3400,12 @@ } }, "node_modules/@radix-ui/react-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.0.tgz", - "integrity": "sha512-3uBAs+egzvJBDZAzvb/n4NxxOYpnspmWxO2u5NbZ8Y6FM/NdrGSF9bop3Cf6F6C71z1rTSn8KV0Fo2ZVd79lGA==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", + "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -3094,22 +3423,22 @@ } }, "node_modules/@radix-ui/react-slider": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.2.1.tgz", - "integrity": "sha512-bEzQoDW0XP+h/oGbutF5VMWJPAl/UU8IJjr7h02SOHDIIIxq+cep8nItVNoBV+OMmahCdqdF38FTpmXoqQUGvw==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.6.tgz", + "integrity": "sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==", "license": "MIT", "dependencies": { - "@radix-ui/number": "1.1.0", - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-collection": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-previous": "1.1.0", - "@radix-ui/react-use-size": "1.1.0" + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -3127,12 +3456,12 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", - "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0" + "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -3145,18 +3474,18 @@ } }, "node_modules/@radix-ui/react-switch": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.1.1.tgz", - "integrity": "sha512-diPqDDoBcZPSicYoMWdWx+bCPuTRH4QSp9J+65IvtdS0Kuzt67bI6n32vCj8q6NZmYW/ah+2orOtMwcX5eQwIg==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", + "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-previous": "1.1.0", - "@radix-ui/react-use-size": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -3174,19 +3503,19 @@ } }, "node_modules/@radix-ui/react-tabs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.1.tgz", - "integrity": "sha512-3GBUDmP2DvzmtYLMsHmpA1GtR46ZDZ+OreXM/N+kkQJOPIgytFWWTfDQmBQKBvaFS0Vno0FktdbVzN28KGrMdw==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-roving-focus": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -3204,23 +3533,23 @@ } }, "node_modules/@radix-ui/react-toast": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.2.tgz", - "integrity": "sha512-Z6pqSzmAP/bFJoqMAston4eSNa+ud44NSZTiZUmUen+IOZ5nBY8kzuU5WDBVyFXPtcW6yUalOHsxM/BP6Sv8ww==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", + "integrity": "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-collection": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.1", - "@radix-ui/react-portal": "1.1.2", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -3238,14 +3567,14 @@ } }, "node_modules/@radix-ui/react-toggle": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.0.tgz", - "integrity": "sha512-gwoxaKZ0oJ4vIgzsfESBuSgJNdc0rv12VhHgcqN0TEJmmZixXG/2XpsLK8kzNWYcnaoRIEEQc0bEi3dIvdUpjw==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz", + "integrity": "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -3263,18 +3592,18 @@ } }, "node_modules/@radix-ui/react-toggle-group": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.0.tgz", - "integrity": "sha512-PpTJV68dZU2oqqgq75Uzto5o/XfOVgkrJ9rulVmfTKxWp3HfUjHE6CP/WLRR4AzPX9HWxw7vFow2me85Yu+Naw==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.11.tgz", + "integrity": "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-roving-focus": "1.1.0", - "@radix-ui/react-toggle": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-toggle": "1.1.10", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -3291,38 +3620,24 @@ } } }, - "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/react-context": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", - "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-tooltip": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.4.tgz", - "integrity": "sha512-QpObUH/ZlpaO4YgHSaYzrLO2VuO+ZBFFgGzjMUPwtiYnAzzNNDPJeEGRrT7qNOrWm/Jr08M1vlp+vTHtnSQ0Uw==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", + "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", + "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.1", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.0", - "@radix-ui/react-portal": "1.1.2", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -3340,9 +3655,9 @@ } }, "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", - "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -3355,12 +3670,31 @@ } }, "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", - "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.0" + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -3373,12 +3707,30 @@ } }, "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", - "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.0" + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-is-hydrated": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", + "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@types/react": "*", @@ -3391,9 +3743,9 @@ } }, "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", - "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -3406,9 +3758,9 @@ } }, "node_modules/@radix-ui/react-use-previous": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", - "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -3421,12 +3773,12 @@ } }, "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", - "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", "license": "MIT", "dependencies": { - "@radix-ui/rect": "1.1.0" + "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -3439,12 +3791,12 @@ } }, "node_modules/@radix-ui/react-use-size": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", - "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -3457,12 +3809,12 @@ } }, "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz", - "integrity": "sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -3480,349 +3832,208 @@ } }, "node_modules/@radix-ui/rect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", - "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, "node_modules/@remix-run/router": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz", - "integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", + "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/@reown/appkit": { - "version": "1.7.17", - "resolved": "https://registry.npmjs.org/@reown/appkit/-/appkit-1.7.17.tgz", - "integrity": "sha512-gME4Ery7HGTNEGzLckWP7qfD2ec/1UEuUkcGskGeisUnGcAsPH9z2deFFX1szialsgzTNU4/H5ZGdWqZQA8p2w==", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@reown/appkit/-/appkit-1.8.6.tgz", + "integrity": "sha512-3dXht+l6FtSu7yMC4TlCYySLXVC5fLIfSHvgWExhLMzgSF4f5ZKKO36K2rAyxBHMcJatRJiRUzXMgackQZ7cRg==", "hasInstallScript": true, - "license": "Apache-2.0", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@reown/appkit-common": "1.7.17", - "@reown/appkit-controllers": "1.7.17", - "@reown/appkit-pay": "1.7.17", - "@reown/appkit-polyfills": "1.7.17", - "@reown/appkit-scaffold-ui": "1.7.17", - "@reown/appkit-ui": "1.7.17", - "@reown/appkit-utils": "1.7.17", - "@reown/appkit-wallet": "1.7.17", - "@walletconnect/universal-provider": "2.21.5", + "@reown/appkit-common": "1.8.6", + "@reown/appkit-controllers": "1.8.6", + "@reown/appkit-pay": "1.8.6", + "@reown/appkit-polyfills": "1.8.6", + "@reown/appkit-scaffold-ui": "1.8.6", + "@reown/appkit-ui": "1.8.6", + "@reown/appkit-utils": "1.8.6", + "@reown/appkit-wallet": "1.8.6", + "@walletconnect/universal-provider": "2.21.7", "bs58": "6.0.0", "semver": "7.7.2", - "valtio": "2.1.5", - "viem": ">=2.32.0" + "valtio": "2.1.7", + "viem": ">=2.37.2" }, "optionalDependencies": { - "@lit/react": "1.0.8", - "@reown/appkit-siwx": "1.7.17" + "@lit/react": "1.0.8" } }, "node_modules/@reown/appkit-adapter-bitcoin": { - "version": "1.7.17", - "resolved": "https://registry.npmjs.org/@reown/appkit-adapter-bitcoin/-/appkit-adapter-bitcoin-1.7.17.tgz", - "integrity": "sha512-X/KCzGee4Y0w9FTn9qU7UGrKsldm1zVmJ4qKJz7xLQSkCD07UG6JoHGyySpJ7j5JzkRnqnQtXeH57IDnJpcN8g==", - "license": "Apache-2.0", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@reown/appkit-adapter-bitcoin/-/appkit-adapter-bitcoin-1.8.6.tgz", + "integrity": "sha512-6IPVZVsfiyqaJ7zB8G2owfQHTA/gbqjrXqwhCUU2WD+5dkGSb3rCL+AVi4rpyvtgo9GyhTcYNmYWlZR1QkUYow==", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@exodus/bitcoin-wallet-standard": "0.0.0", - "@reown/appkit": "1.7.17", - "@reown/appkit-common": "1.7.17", - "@reown/appkit-controllers": "1.7.17", - "@reown/appkit-polyfills": "1.7.17", - "@reown/appkit-utils": "1.7.17", + "@reown/appkit": "1.8.6", + "@reown/appkit-common": "1.8.6", + "@reown/appkit-controllers": "1.8.6", + "@reown/appkit-polyfills": "1.8.6", + "@reown/appkit-utils": "1.8.6", "@wallet-standard/app": "1.1.0", "@wallet-standard/base": "1.1.0", - "@walletconnect/universal-provider": "2.21.5", + "@walletconnect/universal-provider": "2.21.7", "bitcoinjs-lib": "6.1.7", "sats-connect": "3.5.0" } }, "node_modules/@reown/appkit-adapter-wagmi": { - "version": "1.7.17", - "resolved": "https://registry.npmjs.org/@reown/appkit-adapter-wagmi/-/appkit-adapter-wagmi-1.7.17.tgz", - "integrity": "sha512-QGAomIvnHKA5mISDISKSJESdWGd4LE6c3cJZ46y29Ta+irmALd90nDQQ0nV9FThNXM8te6qNF4coMNwRn4WuYg==", - "license": "Apache-2.0", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@reown/appkit-adapter-wagmi/-/appkit-adapter-wagmi-1.8.6.tgz", + "integrity": "sha512-amAw0e5Se81At2LpXRMoPvglC0fjsEYkasai8HzVN9hYpEJq45DWow+gUdxg24dEujRjcn6aLuA7yVSu3cYX5A==", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@reown/appkit": "1.7.17", - "@reown/appkit-common": "1.7.17", - "@reown/appkit-controllers": "1.7.17", - "@reown/appkit-polyfills": "1.7.17", - "@reown/appkit-scaffold-ui": "1.7.17", - "@reown/appkit-utils": "1.7.17", - "@reown/appkit-wallet": "1.7.17", - "@walletconnect/universal-provider": "2.21.5", - "valtio": "2.1.5" + "@reown/appkit": "1.8.6", + "@reown/appkit-common": "1.8.6", + "@reown/appkit-controllers": "1.8.6", + "@reown/appkit-polyfills": "1.8.6", + "@reown/appkit-scaffold-ui": "1.8.6", + "@reown/appkit-utils": "1.8.6", + "@reown/appkit-wallet": "1.8.6", + "@walletconnect/universal-provider": "2.21.7", + "valtio": "2.1.7" }, "optionalDependencies": { - "@wagmi/connectors": ">=5.7.11" + "@wagmi/connectors": ">=5.9.9" }, "peerDependencies": { - "@wagmi/core": ">=2.16.7", - "viem": ">=2.32.0", - "wagmi": ">=2.15.7" + "@wagmi/core": ">=2.20.3", + "viem": ">=2.37.1", + "wagmi": ">=2.16.9" } }, "node_modules/@reown/appkit-common": { - "version": "1.7.17", - "resolved": "https://registry.npmjs.org/@reown/appkit-common/-/appkit-common-1.7.17.tgz", - "integrity": "sha512-zfrlNosQ5XBGC7OBG56+lur0nJWCdRKoWVlUnr0dCVVfBmHIgdhFkRNzDqrX/zGqg4OoWDQLO7qaGiijRskfBQ==", - "license": "Apache-2.0", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@reown/appkit-common/-/appkit-common-1.8.6.tgz", + "integrity": "sha512-A4U/80u+ELqWYKDF/OWXHa4+O2BMILvZSyuXEt2NrNzozrMfUB3yq12jXGkqJHkiv7E8smo0khxneX3cSCGuaA==", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "big.js": "6.2.2", "dayjs": "1.11.13", - "viem": ">=2.32.0" + "viem": ">=2.37.2" } }, "node_modules/@reown/appkit-controllers": { - "version": "1.7.17", - "resolved": "https://registry.npmjs.org/@reown/appkit-controllers/-/appkit-controllers-1.7.17.tgz", - "integrity": "sha512-rYgXf3nAzxgu1s10rSfibpAqnm/Y3wyY47v6BpN98Y57NArWqxYXhBtdRQL1ZKpSTV9OmrzwMxPNKePOmFgxZQ==", - "license": "Apache-2.0", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@reown/appkit-controllers/-/appkit-controllers-1.8.6.tgz", + "integrity": "sha512-+ftwSvT8VcMjCJXwf7IfOAp/5lJpJka7Y1nps+iiAE1J1+20BD3yOQ50TUhyCczIegutbuDINlVPUnYufe3a1A==", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@reown/appkit-common": "1.7.17", - "@reown/appkit-wallet": "1.7.17", - "@walletconnect/universal-provider": "2.21.5", - "valtio": "2.1.5", - "viem": ">=2.32.0" + "@reown/appkit-common": "1.8.6", + "@reown/appkit-wallet": "1.8.6", + "@walletconnect/universal-provider": "2.21.7", + "valtio": "2.1.7", + "viem": ">=2.37.2" } }, "node_modules/@reown/appkit-pay": { - "version": "1.7.17", - "resolved": "https://registry.npmjs.org/@reown/appkit-pay/-/appkit-pay-1.7.17.tgz", - "integrity": "sha512-RukQ5oZ+zGzWy9gu4butVcscZ9GB9/h6zmQFXDo9qkAbOicwZKaLR5XMKrjLQIYisu+ODV/ff6NuxnUYs+/r9Q==", - "license": "Apache-2.0", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@reown/appkit-pay/-/appkit-pay-1.8.6.tgz", + "integrity": "sha512-fU80ENaKBknYctDnN7RZYLjyW2B29ZGfwIy5x7sEDny9WBlMVAHgG1dAKEKlU6WkYEM5aTaUe1tl4NhhwPpVVg==", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@reown/appkit-common": "1.7.17", - "@reown/appkit-controllers": "1.7.17", - "@reown/appkit-ui": "1.7.17", - "@reown/appkit-utils": "1.7.17", + "@reown/appkit-common": "1.8.6", + "@reown/appkit-controllers": "1.8.6", + "@reown/appkit-ui": "1.8.6", + "@reown/appkit-utils": "1.8.6", "lit": "3.3.0", - "valtio": "2.1.5" + "valtio": "2.1.7" } }, "node_modules/@reown/appkit-polyfills": { - "version": "1.7.17", - "resolved": "https://registry.npmjs.org/@reown/appkit-polyfills/-/appkit-polyfills-1.7.17.tgz", - "integrity": "sha512-vWRIYS+wc2ByWKn76KMV7zxqTvQ+512KwXAKQcRulu13AdKvnBbr0eYx+ctvSKL+kZoAp9zj4R3RulX3eXnJ8Q==", - "license": "Apache-2.0", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@reown/appkit-polyfills/-/appkit-polyfills-1.8.6.tgz", + "integrity": "sha512-aoBkQSHCZvoLgdHfZfeOLswrYHIPGhhknRMUk8UHDhQsoALCvIEYrenhgIDZUSfkJC//z+4OfQO/yVq3g3vecA==", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "buffer": "6.0.3" } }, "node_modules/@reown/appkit-scaffold-ui": { - "version": "1.7.17", - "resolved": "https://registry.npmjs.org/@reown/appkit-scaffold-ui/-/appkit-scaffold-ui-1.7.17.tgz", - "integrity": "sha512-7nk8DEHQf9/7Ij8Eo85Uj1D/3M9Ybq/LjXyePyaGusZ9E8gf4u/UjKpQK7cTfMNsNl4nrB2mBI9Tk/rwNECdCg==", - "license": "Apache-2.0", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@reown/appkit-scaffold-ui/-/appkit-scaffold-ui-1.8.6.tgz", + "integrity": "sha512-H4NCFBv5fII49pEdJYaIXeujpwJ3WLVAmyaC4i+BxCpDZ8Us8TtNhYoH2ziEqJvsHwC6BhZ54CAlTOszjiww1w==", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@reown/appkit-common": "1.7.17", - "@reown/appkit-controllers": "1.7.17", - "@reown/appkit-ui": "1.7.17", - "@reown/appkit-utils": "1.7.17", - "@reown/appkit-wallet": "1.7.17", + "@reown/appkit-common": "1.8.6", + "@reown/appkit-controllers": "1.8.6", + "@reown/appkit-ui": "1.8.6", + "@reown/appkit-utils": "1.8.6", + "@reown/appkit-wallet": "1.8.6", "lit": "3.3.0" } }, - "node_modules/@reown/appkit-siwx": { - "version": "1.7.17", - "resolved": "https://registry.npmjs.org/@reown/appkit-siwx/-/appkit-siwx-1.7.17.tgz", - "integrity": "sha512-frTTDnj5111+ZNNyHmEWeXiX0IWFlRhP240kmxKTamLElc2PdLUfQq/1yX8Y3bUBHryISjcQYzEtWSEI2oRYKA==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@reown/appkit-common": "1.7.17", - "@reown/appkit-controllers": "1.7.17", - "@reown/appkit-scaffold-ui": "1.7.17", - "@reown/appkit-ui": "1.7.17", - "@reown/appkit-utils": "1.7.17", - "bip322-js": "2.0.0", - "bs58": "6.0.0", - "tweetnacl": "1.0.3", - "viem": "2.32.0" - }, - "peerDependencies": { - "lit": "3.3.0" - } - }, - "node_modules/@reown/appkit-siwx/node_modules/@scure/bip32": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", - "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", - "license": "MIT", - "optional": true, - "dependencies": { - "@noble/curves": "~1.9.0", - "@noble/hashes": "~1.8.0", - "@scure/base": "~1.2.5" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@reown/appkit-siwx/node_modules/@scure/bip39": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", - "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", - "license": "MIT", - "optional": true, - "dependencies": { - "@noble/hashes": "~1.8.0", - "@scure/base": "~1.2.5" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@reown/appkit-siwx/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT", - "optional": true - }, - "node_modules/@reown/appkit-siwx/node_modules/ox": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/ox/-/ox-0.8.1.tgz", - "integrity": "sha512-e+z5epnzV+Zuz91YYujecW8cF01mzmrUtWotJ0oEPym/G82uccs7q0WDHTYL3eiONbTUEvcZrptAKLgTBD3u2A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/wevm" - } - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@adraffy/ens-normalize": "^1.11.0", - "@noble/ciphers": "^1.3.0", - "@noble/curves": "^1.9.1", - "@noble/hashes": "^1.8.0", - "@scure/bip32": "^1.7.0", - "@scure/bip39": "^1.6.0", - "abitype": "^1.0.8", - "eventemitter3": "5.0.1" - }, - "peerDependencies": { - "typescript": ">=5.4.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@reown/appkit-siwx/node_modules/viem": { - "version": "2.32.0", - "resolved": "https://registry.npmjs.org/viem/-/viem-2.32.0.tgz", - "integrity": "sha512-pHwKXQSyEWX+8ttOQJdU5dSBfYd6L9JxARY/Sx0MBj3uF/Zaiqt6o1SbzjFjQXkNzWSgtxK7H89ZI1SMIA2iLQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/wevm" - } - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@noble/curves": "1.9.2", - "@noble/hashes": "1.8.0", - "@scure/bip32": "1.7.0", - "@scure/bip39": "1.6.0", - "abitype": "1.0.8", - "isows": "1.0.7", - "ox": "0.8.1", - "ws": "8.18.2" - }, - "peerDependencies": { - "typescript": ">=5.0.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@reown/appkit-siwx/node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/@reown/appkit-ui": { - "version": "1.7.17", - "resolved": "https://registry.npmjs.org/@reown/appkit-ui/-/appkit-ui-1.7.17.tgz", - "integrity": "sha512-7lscJjtFZIfdcUv5zAsmgiFG2dMziQE0IfqY3U/H5qhnGW8v4ITcTi1gNS3A4lQrNDbcA083LecfVdyKnTdi1A==", - "license": "Apache-2.0", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@reown/appkit-ui/-/appkit-ui-1.8.6.tgz", + "integrity": "sha512-c2/UYGRoI/nayqiPp+nxxw7Wohdzl7Z1J14/p/HHPYyl1/qtV0DaHZW8ZA3NtxfmFGny7eE4fRz5YXRwNDuzAw==", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@reown/appkit-common": "1.7.17", - "@reown/appkit-controllers": "1.7.17", - "@reown/appkit-wallet": "1.7.17", + "@phosphor-icons/webcomponents": "2.1.5", + "@reown/appkit-common": "1.8.6", + "@reown/appkit-controllers": "1.8.6", + "@reown/appkit-wallet": "1.8.6", "lit": "3.3.0", "qrcode": "1.5.3" } }, "node_modules/@reown/appkit-utils": { - "version": "1.7.17", - "resolved": "https://registry.npmjs.org/@reown/appkit-utils/-/appkit-utils-1.7.17.tgz", - "integrity": "sha512-QWzHTmSDFy90Bp5pUUQASzcjnJXPiEvasJV68j3PZifenTPDCfFW+VsiHduWNodTHAA/rZ12O3uBQE+stM3xmQ==", - "license": "Apache-2.0", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@reown/appkit-utils/-/appkit-utils-1.8.6.tgz", + "integrity": "sha512-MpnCsQEsDh3SMU0+0Vsn5aX/2dfO+oNTvwPz4hvb35sHqR1NoP/CO0IzCApLjc84TXqbX5giavzgME5s3QL3Jw==", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@reown/appkit-common": "1.7.17", - "@reown/appkit-controllers": "1.7.17", - "@reown/appkit-polyfills": "1.7.17", - "@reown/appkit-wallet": "1.7.17", + "@reown/appkit-common": "1.8.6", + "@reown/appkit-controllers": "1.8.6", + "@reown/appkit-polyfills": "1.8.6", + "@reown/appkit-wallet": "1.8.6", "@wallet-standard/wallet": "1.1.0", "@walletconnect/logger": "2.1.2", - "@walletconnect/universal-provider": "2.21.5", - "valtio": "2.1.5", - "viem": ">=2.32.0" + "@walletconnect/universal-provider": "2.21.7", + "valtio": "2.1.7", + "viem": ">=2.37.2" }, "peerDependencies": { - "valtio": "2.1.5" + "valtio": "2.1.7" } }, "node_modules/@reown/appkit-wallet": { - "version": "1.7.17", - "resolved": "https://registry.npmjs.org/@reown/appkit-wallet/-/appkit-wallet-1.7.17.tgz", - "integrity": "sha512-tgIqHZZJISGCir0reQ/pXcIKXuP7JNqSuEDunfi5whNJi6z27h3g468RGk1Zo+MC//DRnQb01xMrv+iWRr8mCQ==", - "license": "Apache-2.0", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@reown/appkit-wallet/-/appkit-wallet-1.8.6.tgz", + "integrity": "sha512-m0Um+PLeWEHsW8saBWdjkROFs5DRI++SWb+NL+k4Ys2+PfhH9adAbV8o6SJEUzG4XqWhQsS5QrpNo4ZJIfyu5g==", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@reown/appkit-common": "1.7.17", - "@reown/appkit-polyfills": "1.7.17", + "@reown/appkit-common": "1.8.6", + "@reown/appkit-polyfills": "1.8.6", "@walletconnect/logger": "2.1.2", "zod": "3.22.4" } }, "node_modules/@reown/appkit-wallet-button": { - "version": "1.7.17", - "resolved": "https://registry.npmjs.org/@reown/appkit-wallet-button/-/appkit-wallet-button-1.7.17.tgz", - "integrity": "sha512-4W/Joqyqkwyj3VtGkDgyLvZXwcuwLQpcJ76KuKPIYqRwjv6VvvpN3FLr3C0KXu6hYQ/o9DXCOxe03/SckAu3cw==", - "license": "Apache-2.0", + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@reown/appkit-wallet-button/-/appkit-wallet-button-1.8.6.tgz", + "integrity": "sha512-D4TIJRlMAgOerCO+iNllkxQoTsDYZzto2QXwDTyAfAYIz7yV+2L1DK/vRevoa/U6wikyXnNXiDAtUvKhoOh5IQ==", + "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@reown/appkit-common": "1.7.17", - "@reown/appkit-controllers": "1.7.17", - "@reown/appkit-ui": "1.7.17", - "@reown/appkit-utils": "1.7.17", + "@reown/appkit-common": "1.8.6", + "@reown/appkit-controllers": "1.8.6", + "@reown/appkit-ui": "1.8.6", + "@reown/appkit-utils": "1.8.6", "lit": "3.3.0", - "valtio": "2.1.5" + "valtio": "2.1.7" }, "optionalDependencies": { "@lit/react": "1.0.8" @@ -3837,10 +4048,17 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", - "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.2.tgz", + "integrity": "sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==", "cpu": [ "arm" ], @@ -3852,9 +4070,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", - "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.2.tgz", + "integrity": "sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==", "cpu": [ "arm64" ], @@ -3866,9 +4084,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", - "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.2.tgz", + "integrity": "sha512-OZuTVTpj3CDSIxmPgGH8en/XtirV5nfljHZ3wrNwvgkT5DQLhIKAeuFSiwtbMto6oVexV0k1F1zqURPKf5rI1Q==", "cpu": [ "arm64" ], @@ -3880,9 +4098,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", - "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.2.tgz", + "integrity": "sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==", "cpu": [ "x64" ], @@ -3893,10 +4111,38 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.2.tgz", + "integrity": "sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.2.tgz", + "integrity": "sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", - "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.2.tgz", + "integrity": "sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==", "cpu": [ "arm" ], @@ -3908,9 +4154,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", - "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.2.tgz", + "integrity": "sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==", "cpu": [ "arm" ], @@ -3922,9 +4168,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", - "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.2.tgz", + "integrity": "sha512-df0Eou14ojtUdLQdPFnymEQteENwSJAdLf5KCDrmZNsy1c3YaCNaJvYsEUHnrg+/DLBH612/R0xd3dD03uz2dg==", "cpu": [ "arm64" ], @@ -3936,9 +4182,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", - "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.2.tgz", + "integrity": "sha512-iPeouV0UIDtz8j1YFR4OJ/zf7evjauqv7jQ/EFs0ClIyL+by++hiaDAfFipjOgyz6y6xbDvJuiU4HwpVMpRFDQ==", "cpu": [ "arm64" ], @@ -3949,10 +4195,24 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", - "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.50.2.tgz", + "integrity": "sha512-OL6KaNvBopLlj5fTa5D5bau4W82f+1TyTZRr2BdnfsrnQnmdxh4okMxR2DcDkJuh4KeoQZVuvHvzuD/lyLn2Kw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.2.tgz", + "integrity": "sha512-I21VJl1w6z/K5OTRl6aS9DDsqezEZ/yKpbqlvfHbW0CEF5IL8ATBMuUx6/mp683rKTK8thjs/0BaNrZLXetLag==", "cpu": [ "ppc64" ], @@ -3964,9 +4224,23 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", - "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.2.tgz", + "integrity": "sha512-Hq6aQJT/qFFHrYMjS20nV+9SKrXL2lvFBENZoKfoTH2kKDOJqff5OSJr4x72ZaG/uUn+XmBnGhfr4lwMRrmqCQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.2.tgz", + "integrity": "sha512-82rBSEXRv5qtKyr0xZ/YMF531oj2AIpLZkeNYxmKNN6I2sVE9PGegN99tYDLK2fYHJITL1P2Lgb4ZXnv0PjQvw==", "cpu": [ "riscv64" ], @@ -3978,9 +4252,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", - "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.2.tgz", + "integrity": "sha512-4Q3S3Hy7pC6uaRo9gtXUTJ+EKo9AKs3BXKc2jYypEcMQ49gDPFU2P1ariX9SEtBzE5egIX6fSUmbmGazwBVF9w==", "cpu": [ "s390x" ], @@ -3992,9 +4266,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", - "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.2.tgz", + "integrity": "sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA==", "cpu": [ "x64" ], @@ -4006,9 +4280,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", - "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.2.tgz", + "integrity": "sha512-HPNJwxPL3EmhzeAnsWQCM3DcoqOz3/IC6de9rWfGR8ZCuEHETi9km66bH/wG3YH0V3nyzyFEGUZeL5PKyy4xvw==", "cpu": [ "x64" ], @@ -4019,10 +4293,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.2.tgz", + "integrity": "sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", - "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.2.tgz", + "integrity": "sha512-eFUvvnTYEKeTyHEijQKz81bLrUQOXKZqECeiWH6tb8eXXbZk+CXSG2aFrig2BQ/pjiVRj36zysjgILkqarS2YA==", "cpu": [ "arm64" ], @@ -4034,9 +4322,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", - "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.2.tgz", + "integrity": "sha512-cBaWmXqyfRhH8zmUxK3d3sAhEWLrtMjWBRwdMMHJIXSjvjLKvv49adxiEz+FJ8AP90apSDDBx2Tyd/WylV6ikA==", "cpu": [ "ia32" ], @@ -4048,9 +4336,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", - "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.2.tgz", + "integrity": "sha512-APwKy6YUhvZaEoHyM+9xqmTpviEI+9eL7LoCH+aLcvWYHJ663qG5zx7WzWZY+a9qkg5JtzcMyJ9z0WtQBMDmgA==", "cpu": [ "x64" ], @@ -4235,15 +4523,15 @@ "license": "MIT" }, "node_modules/@swc/core": { - "version": "1.7.39", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.39.tgz", - "integrity": "sha512-jns6VFeOT49uoTKLWIEfiQqJAlyqldNAt80kAr8f7a5YjX0zgnG3RBiLMpksx4Ka4SlK4O6TJ/lumIM3Trp82g==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz", + "integrity": "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.13" + "@swc/types": "^0.1.24" }, "engines": { "node": ">=10" @@ -4253,19 +4541,19 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.7.39", - "@swc/core-darwin-x64": "1.7.39", - "@swc/core-linux-arm-gnueabihf": "1.7.39", - "@swc/core-linux-arm64-gnu": "1.7.39", - "@swc/core-linux-arm64-musl": "1.7.39", - "@swc/core-linux-x64-gnu": "1.7.39", - "@swc/core-linux-x64-musl": "1.7.39", - "@swc/core-win32-arm64-msvc": "1.7.39", - "@swc/core-win32-ia32-msvc": "1.7.39", - "@swc/core-win32-x64-msvc": "1.7.39" + "@swc/core-darwin-arm64": "1.13.5", + "@swc/core-darwin-x64": "1.13.5", + "@swc/core-linux-arm-gnueabihf": "1.13.5", + "@swc/core-linux-arm64-gnu": "1.13.5", + "@swc/core-linux-arm64-musl": "1.13.5", + "@swc/core-linux-x64-gnu": "1.13.5", + "@swc/core-linux-x64-musl": "1.13.5", + "@swc/core-win32-arm64-msvc": "1.13.5", + "@swc/core-win32-ia32-msvc": "1.13.5", + "@swc/core-win32-x64-msvc": "1.13.5" }, "peerDependencies": { - "@swc/helpers": "*" + "@swc/helpers": ">=0.5.17" }, "peerDependenciesMeta": { "@swc/helpers": { @@ -4274,9 +4562,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.7.39", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.39.tgz", - "integrity": "sha512-o2nbEL6scMBMCTvY9OnbyVXtepLuNbdblV9oNJEFia5v5eGj9WMrnRQiylH3Wp/G2NYkW7V1/ZVW+kfvIeYe9A==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.5.tgz", + "integrity": "sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==", "cpu": [ "arm64" ], @@ -4291,9 +4579,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.7.39", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.39.tgz", - "integrity": "sha512-qMlv3XPgtPi/Fe11VhiPDHSLiYYk2dFYl747oGsHZPq+6tIdDQjIhijXPcsUHIXYDyG7lNpODPL8cP/X1sc9MA==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.5.tgz", + "integrity": "sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==", "cpu": [ "x64" ], @@ -4308,9 +4596,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.7.39", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.39.tgz", - "integrity": "sha512-NP+JIkBs1ZKnpa3Lk2W1kBJMwHfNOxCUJXuTa2ckjFsuZ8OUu2gwdeLFkTHbR43dxGwH5UzSmuGocXeMowra/Q==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.5.tgz", + "integrity": "sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==", "cpu": [ "arm" ], @@ -4325,9 +4613,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.7.39", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.39.tgz", - "integrity": "sha512-cPc+/HehyHyHcvAsk3ML/9wYcpWVIWax3YBaA+ScecJpSE04l/oBHPfdqKUPslqZ+Gcw0OWnIBGJT/fBZW2ayw==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.5.tgz", + "integrity": "sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==", "cpu": [ "arm64" ], @@ -4342,9 +4630,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.7.39", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.39.tgz", - "integrity": "sha512-8RxgBC6ubFem66bk9XJ0vclu3exJ6eD7x7CwDhp5AD/tulZslTYXM7oNPjEtje3xxabXuj/bEUMNvHZhQRFdqA==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.5.tgz", + "integrity": "sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==", "cpu": [ "arm64" ], @@ -4359,9 +4647,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.7.39", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.39.tgz", - "integrity": "sha512-3gtCPEJuXLQEolo9xsXtuPDocmXQx12vewEyFFSMSjOfakuPOBmOQMa0sVL8Wwius8C1eZVeD1fgk0omMqeC+Q==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.5.tgz", + "integrity": "sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==", "cpu": [ "x64" ], @@ -4376,9 +4664,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.7.39", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.39.tgz", - "integrity": "sha512-mg39pW5x/eqqpZDdtjZJxrUvQNSvJF4O8wCl37fbuFUqOtXs4TxsjZ0aolt876HXxxhsQl7rS+N4KioEMSgTZw==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.5.tgz", + "integrity": "sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==", "cpu": [ "x64" ], @@ -4393,9 +4681,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.7.39", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.39.tgz", - "integrity": "sha512-NZwuS0mNJowH3e9bMttr7B1fB8bW5svW/yyySigv9qmV5VcQRNz1kMlCvrCLYRsa93JnARuiaBI6FazSeG8mpA==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.5.tgz", + "integrity": "sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==", "cpu": [ "arm64" ], @@ -4410,9 +4698,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.7.39", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.39.tgz", - "integrity": "sha512-qFmvv5UExbJPXhhvCVDBnjK5Duqxr048dlVB6ZCgGzbRxuarOlawCzzLK4N172230pzlAWGLgn9CWl3+N6zfHA==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.5.tgz", + "integrity": "sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==", "cpu": [ "ia32" ], @@ -4427,9 +4715,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.7.39", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.39.tgz", - "integrity": "sha512-o+5IMqgOtj9+BEOp16atTfBgCogVak9svhBpwsbcJQp67bQbxGYhAPPDW/hZ2rpSSF7UdzbY9wudoX9G4trcuQ==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.5.tgz", + "integrity": "sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==", "cpu": [ "x64" ], @@ -4451,9 +4739,9 @@ "license": "Apache-2.0" }, "node_modules/@swc/types": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.13.tgz", - "integrity": "sha512-JL7eeCk6zWCbiYQg2xQSdLXQJl8Qoc9rXmG2cEKvHe3CKwMHwHGpfOb8frzNLmbycOo6I51qxnLnn9ESf4I20Q==", + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", + "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4476,23 +4764,10 @@ "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, - "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@tanstack/query-core": { - "version": "5.83.1", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.83.1.tgz", - "integrity": "sha512-OG69LQgT7jSp+5pPuCfzltq/+7l2xoweggjme9vlbCPa/d7D7zaqv5vN/S82SzSYZ4EDLTxNO1PWrv49RAS64Q==", + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.89.0.tgz", + "integrity": "sha512-joFV1MuPhSLsKfTzwjmPDrp8ENfZ9N23ymFu07nLfn3JCkSHy0CFgsyhHTJOmWaumC/WiNIKM0EJyduCF/Ih/Q==", "license": "MIT", "funding": { "type": "github", @@ -4500,12 +4775,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.84.1", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.84.1.tgz", - "integrity": "sha512-zo7EUygcWJMQfFNWDSG7CBhy8irje/XY0RDVKKV4IQJAysb+ZJkkJPcnQi+KboyGUgT+SQebRFoTqLuTtfoDLw==", + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.89.0.tgz", + "integrity": "sha512-SXbtWSTSRXyBOe80mszPxpEbaN4XPRUp/i0EfQK1uyj3KCk/c8FuPJNIRwzOVe/OU3rzxrYtiNabsAmk1l714A==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.83.1" + "@tanstack/query-core": "5.89.0" }, "funding": { "type": "github", @@ -4516,9 +4791,9 @@ } }, "node_modules/@testing-library/dom": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", - "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", "peer": true, @@ -4527,9 +4802,9 @@ "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", - "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", + "picocolors": "1.1.1", "pretty-format": "^27.0.2" }, "engines": { @@ -4537,18 +4812,17 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", - "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.8.0.tgz", + "integrity": "sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==", "dev": true, "license": "MIT", "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", - "chalk": "^3.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", - "lodash": "^4.17.21", + "picocolors": "^1.1.1", "redent": "^3.0.0" }, "engines": { @@ -4557,20 +4831,6 @@ "yarn": ">=1" } }, - "node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", @@ -4639,9 +4899,9 @@ } }, "node_modules/@types/d3-array": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", - "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", "license": "MIT" }, "node_modules/@types/d3-color": { @@ -4666,33 +4926,33 @@ } }, "node_modules/@types/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", "license": "MIT" }, "node_modules/@types/d3-scale": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", - "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", "license": "MIT", "dependencies": { "@types/d3-time": "*" } }, "node_modules/@types/d3-shape": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", - "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", "license": "MIT", "dependencies": { "@types/d3-path": "*" } }, "node_modules/@types/d3-time": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", - "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", "license": "MIT" }, "node_modules/@types/d3-timer": { @@ -4717,19 +4977,10 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/dns-packet": { - "version": "5.6.5", - "resolved": "https://registry.npmjs.org/@types/dns-packet/-/dns-packet-5.6.5.tgz", - "integrity": "sha512-qXOC7XLOEe43ehtWJCMnQXvgcIpv6rPmQ1jXT98Ad8A3TB1Ue50jsCbSSSyuazScEuZ/Q026vHbrOTVkmwA+7Q==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "license": "MIT" }, "node_modules/@types/estree-jsx": { @@ -4757,6 +5008,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", + "license": "MIT" + }, + "node_modules/@types/lodash.debounce": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.9.tgz", + "integrity": "sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -4773,24 +5039,24 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.7.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.9.tgz", - "integrity": "sha512-jrTfRC7FM6nChvU7X2KqcrgquofrWLFDeYC1hKfwNWomVvrn7JIksqf344WN2X/y8xrgqBd2dJATZV4GbatBfg==", + "version": "20.19.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.17.tgz", + "integrity": "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==", "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.21.0" } }, "node_modules/@types/prop-types": { - "version": "15.7.13", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.12", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", - "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", + "version": "18.3.24", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz", + "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==", "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -4798,13 +5064,13 @@ } }, "node_modules/@types/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", - "dependencies": { - "@types/react": "*" + "peerDependencies": { + "@types/react": "^18.0.0" } }, "node_modules/@types/retry": { @@ -4813,6 +5079,13 @@ "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", "license": "MIT" }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", @@ -4842,32 +5115,34 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz", - "integrity": "sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.11.0", - "@typescript-eslint/type-utils": "8.11.0", - "@typescript-eslint/utils": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^5.2.4", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -4876,16 +5151,43 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.11.0.tgz", - "integrity": "sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.11.0", - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/typescript-estree": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.0.tgz", + "integrity": "sha512-ZeaGNraRsq10GuEohKTo4295Z/SuGcSq2LzfGlqiuEvfArzo/VRrT0ZaJsVPuKZ55lVbNk8U6FcL+ZMH8CoyVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.44.0", + "@typescript-eslint/types": "^8.44.0", "debug": "^4.3.4" }, "engines": { @@ -4896,44 +5198,47 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.0.tgz", + "integrity": "sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz", - "integrity": "sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz", - "integrity": "sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.0.tgz", + "integrity": "sha512-x5Y0+AuEPqAInc6yd0n5DAcvtoQ/vyaGwuX5HE9n6qAefk1GaedqrLQF8kQGylLUb9pnZyLf+iEiL9fr8APDtQ==", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.11.0", - "@typescript-eslint/utils": "8.11.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -4941,6 +5246,32 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, "peerDependenciesMeta": { "typescript": { "optional": true @@ -4948,13 +5279,13 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.11.0.tgz", - "integrity": "sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -4962,23 +5293,23 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz", - "integrity": "sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", - "fast-glob": "^3.3.2", + "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -4991,9 +5322,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5001,9 +5332,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "license": "ISC", "dependencies": { @@ -5017,40 +5348,43 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.11.0.tgz", - "integrity": "sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.11.0", - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/typescript-estree": "8.11.0" + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz", - "integrity": "sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.11.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -5077,16 +5411,17 @@ "license": "ISC" }, "node_modules/@vitejs/plugin-react-swc": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.7.1.tgz", - "integrity": "sha512-vgWOY0i1EROUK0Ctg1hwhtC3SdcDjZcdit4Ups4aPkDcB1jYhmo+RMYWY87cmXMhvtD5uf8lV89j2w16vkdSVg==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.11.0.tgz", + "integrity": "sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==", "dev": true, "license": "MIT", "dependencies": { - "@swc/core": "^1.7.26" + "@rolldown/pluginutils": "1.0.0-beta.27", + "@swc/core": "^1.12.11" }, "peerDependencies": { - "vite": "^4 || ^5" + "vite": "^4 || ^5 || ^6 || ^7" } }, "node_modules/@vitest/expect": { @@ -5227,13 +5562,14 @@ } }, "node_modules/@wagmi/connectors": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/@wagmi/connectors/-/connectors-5.9.1.tgz", - "integrity": "sha512-o50e6reSYkVi2d72WWwbKSZ7xgLAeQ1Ja64tTWq3UhU1XtJPvQXWieCInIGInOajAAsZsYCPKYrPj6WoSl0Hqw==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@wagmi/connectors/-/connectors-5.10.0.tgz", + "integrity": "sha512-pG9a9PIOkxfJQF7kSdMUfPjZ52LOPjXboga99LnZHh+VXmXd2J93r7NGYO3kF0lhrt3hdbmYYsZCOgbPkXiHig==", "license": "MIT", "dependencies": { "@base-org/account": "1.1.1", "@coinbase/wallet-sdk": "4.3.6", + "@gemini-wallet/core": "0.2.0", "@metamask/sdk": "0.32.0", "@safe-global/safe-apps-provider": "0.18.6", "@safe-global/safe-apps-sdk": "9.1.0", @@ -5244,7 +5580,7 @@ "url": "https://github.com/sponsors/wevm" }, "peerDependencies": { - "@wagmi/core": "2.18.1", + "@wagmi/core": "2.21.0", "typescript": ">=5.0.4", "viem": "2.x" }, @@ -5255,9 +5591,9 @@ } }, "node_modules/@wagmi/core": { - "version": "2.18.1", - "resolved": "https://registry.npmjs.org/@wagmi/core/-/core-2.18.1.tgz", - "integrity": "sha512-mU+qXeeY2/0lq8bf4uFm5RtMrc8FgOToqzMVMf6MzNdNbKxpNlmlbuTyRbyd9cxn4UnYa6+S6Bmx1x42FV7w3g==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@wagmi/core/-/core-2.21.0.tgz", + "integrity": "sha512-ZtQBUvHEyfSM22BCeXZDjwcpby2vZjivjP8mMkQHxuVqRDnunaS5DIlZr2XPJKv01lPdlUajFSoiziNcIZlH5w==", "license": "MIT", "dependencies": { "eventemitter3": "5.0.1", @@ -5281,12 +5617,6 @@ } } }, - "node_modules/@wagmi/core/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, "node_modules/@wagmi/core/node_modules/zustand": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.0.tgz", @@ -5317,17 +5647,17 @@ } }, "node_modules/@waku/core": { - "version": "0.0.39-eb93360.0", - "resolved": "https://registry.npmjs.org/@waku/core/-/core-0.0.39-eb93360.0.tgz", - "integrity": "sha512-0BLrm9SEJVjtRDRjkc3lAY9PessVNd8mIfqqqSaDu3rjP3PGjpWaFRi9KK1s1FFQbiG2kXHwBs2Q3zxYWTvYag==", + "version": "0.0.40-79dfb35.0", + "resolved": "https://registry.npmjs.org/@waku/core/-/core-0.0.40-79dfb35.0.tgz", + "integrity": "sha512-+94ez/4zZDQffAK/ERzXOkqCOlMh28/EEtTmqdaahSLrpc3EgqdIsDC8uUPddkOSDoaJ2Y657gm4WZ4PjyRmOQ==", "license": "MIT OR Apache-2.0", "dependencies": { "@libp2p/ping": "2.0.35", "@noble/hashes": "^1.3.2", - "@waku/enr": "0.0.33-eb93360.0", - "@waku/interfaces": "0.0.34-eb93360.0", - "@waku/proto": "0.0.14-eb93360.0", - "@waku/utils": "0.0.27-eb93360.0", + "@waku/enr": "0.0.34-79dfb35.0", + "@waku/interfaces": "0.0.35-79dfb35.0", + "@waku/proto": "0.0.15-79dfb35.0", + "@waku/utils": "0.0.28-79dfb35.0", "debug": "^4.3.4", "it-all": "^3.0.4", "it-length-prefixed": "^9.0.4", @@ -5382,16 +5712,16 @@ } }, "node_modules/@waku/discovery": { - "version": "0.0.12-eb93360.0", - "resolved": "https://registry.npmjs.org/@waku/discovery/-/discovery-0.0.12-eb93360.0.tgz", - "integrity": "sha512-lFq72Pw6hUZfFdy2ioeEO6dZgJ6U8afDg5EHsnEZUC72vsJMiWmzm5XO5a84Tqt10E079mxKBGA3+cC+Djzz7Q==", + "version": "0.0.13-79dfb35.0", + "resolved": "https://registry.npmjs.org/@waku/discovery/-/discovery-0.0.13-79dfb35.0.tgz", + "integrity": "sha512-YNR7ThBjerUqqK0dOPAartpx5OeQN3r3xmuabUm8SCnKKXFLbB7WFBtSwu3X0O4GoalhtC9fAJhkia91A6CZEQ==", "license": "MIT OR Apache-2.0", "dependencies": { - "@waku/core": "0.0.39-eb93360.0", - "@waku/enr": "0.0.33-eb93360.0", - "@waku/interfaces": "0.0.34-eb93360.0", - "@waku/proto": "0.0.14-eb93360.0", - "@waku/utils": "0.0.27-eb93360.0", + "@waku/core": "0.0.40-79dfb35.0", + "@waku/enr": "0.0.34-79dfb35.0", + "@waku/interfaces": "0.0.35-79dfb35.0", + "@waku/proto": "0.0.15-79dfb35.0", + "@waku/utils": "0.0.28-79dfb35.0", "debug": "^4.3.4", "dns-over-http-resolver": "^3.0.8", "hi-base32": "^0.5.1", @@ -5402,9 +5732,9 @@ } }, "node_modules/@waku/enr": { - "version": "0.0.33-eb93360.0", - "resolved": "https://registry.npmjs.org/@waku/enr/-/enr-0.0.33-eb93360.0.tgz", - "integrity": "sha512-Rejx1qoiO8s5nxxp9AE2PKnatStL8TpgJEL3TwmbhBONW9U4JvP3YvqRMFqhLEV+mbrt9zTon19rfqCOzd+kVg==", + "version": "0.0.34-79dfb35.0", + "resolved": "https://registry.npmjs.org/@waku/enr/-/enr-0.0.34-79dfb35.0.tgz", + "integrity": "sha512-suGFUu94gizghRQ+gNjTqR4zXMDK91FBlIn0EeALwVIboHX1MUExMjd58qgQ8biWdcJpnB/NiyiobK+5X46U7Q==", "license": "MIT OR Apache-2.0", "dependencies": { "@ethersproject/rlp": "^5.7.0", @@ -5412,7 +5742,7 @@ "@libp2p/peer-id": "5.1.7", "@multiformats/multiaddr": "^12.0.0", "@noble/secp256k1": "^1.7.1", - "@waku/utils": "0.0.27-eb93360.0", + "@waku/utils": "0.0.28-79dfb35.0", "debug": "^4.3.4", "js-sha3": "^0.9.2" }, @@ -5456,18 +5786,18 @@ } }, "node_modules/@waku/interfaces": { - "version": "0.0.34-eb93360.0", - "resolved": "https://registry.npmjs.org/@waku/interfaces/-/interfaces-0.0.34-eb93360.0.tgz", - "integrity": "sha512-nMiUgcM86SLLNWxjdHmsQ4m0ONnnF9j0mfnTbj156UZ8QcP3X38BcOjKSXw0y6b+Iv97cqXjNqzJXOwX2BqoPA==", + "version": "0.0.35-79dfb35.0", + "resolved": "https://registry.npmjs.org/@waku/interfaces/-/interfaces-0.0.35-79dfb35.0.tgz", + "integrity": "sha512-3zBBpblsSrLHUAOjkQ+js0G5/TeNS4PERgR587caxn75xNn7Fd8WvgAM1xhMbtQU08twDZFGXKaJu3m9zCOOmA==", "license": "MIT OR Apache-2.0", "engines": { "node": ">=22" } }, "node_modules/@waku/proto": { - "version": "0.0.14-eb93360.0", - "resolved": "https://registry.npmjs.org/@waku/proto/-/proto-0.0.14-eb93360.0.tgz", - "integrity": "sha512-KvPyFVxKRUseOwk2l92BUbc7IqlwPGpe8HMWNoKV3ud7lisJbFz6PnYgDIGVNoVHk0pz75RtyxvgbMTBf5UsYQ==", + "version": "0.0.15-79dfb35.0", + "resolved": "https://registry.npmjs.org/@waku/proto/-/proto-0.0.15-79dfb35.0.tgz", + "integrity": "sha512-lH2a74lNzXKNpALDWIMLFfNMmfJtd+lAFgLhMEu3lHBVCgu1SVXy54fBk3X08A/cfOZSzuW0kStlND6GsYQOyA==", "license": "MIT OR Apache-2.0", "dependencies": { "protons-runtime": "^5.4.0" @@ -5477,9 +5807,9 @@ } }, "node_modules/@waku/sdk": { - "version": "0.0.35-eb93360.0", - "resolved": "https://registry.npmjs.org/@waku/sdk/-/sdk-0.0.35-eb93360.0.tgz", - "integrity": "sha512-9ZmhcG1q54YaLTWnTiTKImFBjLnS1zMM8fMBiBmY1097Jb71u1v8OBYdhsFE00uhOkpt4qMZc2RNrzMZhf+H8g==", + "version": "0.0.36-79dfb35.0", + "resolved": "https://registry.npmjs.org/@waku/sdk/-/sdk-0.0.36-79dfb35.0.tgz", + "integrity": "sha512-mbr8dzHLq5T4K4ShfJwOlPeAY2v9ABvZ5mUbViFsKgfwO14vQV4X80P/lgyiFYwQg8r6ZL7Mc9UkRz/FArgSDg==", "license": "MIT OR Apache-2.0", "dependencies": { "@chainsafe/libp2p-noise": "16.1.3", @@ -5489,28 +5819,30 @@ "@libp2p/ping": "2.0.35", "@libp2p/websockets": "9.2.16", "@noble/hashes": "^1.3.3", - "@waku/core": "0.0.39-eb93360.0", - "@waku/discovery": "0.0.12-eb93360.0", - "@waku/interfaces": "0.0.34-eb93360.0", - "@waku/proto": "0.0.14-eb93360.0", - "@waku/sds": "0.0.7-eb93360.0", - "@waku/utils": "0.0.27-eb93360.0", - "libp2p": "2.8.11" + "@types/lodash.debounce": "^4.0.9", + "@waku/core": "0.0.40-79dfb35.0", + "@waku/discovery": "0.0.13-79dfb35.0", + "@waku/interfaces": "0.0.35-79dfb35.0", + "@waku/proto": "0.0.15-79dfb35.0", + "@waku/sds": "0.0.8-79dfb35.0", + "@waku/utils": "0.0.28-79dfb35.0", + "libp2p": "2.8.11", + "lodash.debounce": "^4.0.8" }, "engines": { "node": ">=22" } }, "node_modules/@waku/sds": { - "version": "0.0.7-eb93360.0", - "resolved": "https://registry.npmjs.org/@waku/sds/-/sds-0.0.7-eb93360.0.tgz", - "integrity": "sha512-vHlEAVbhAh662sCJ88hMgJMdP+KfuNwh4xBAcTzhuyhMFur4aw79Ci0OhAlziDPDyPA9ziCEnnpm2BX4319O2g==", + "version": "0.0.8-79dfb35.0", + "resolved": "https://registry.npmjs.org/@waku/sds/-/sds-0.0.8-79dfb35.0.tgz", + "integrity": "sha512-hhzlLwgSsDYYmIBBZprJWkCUkI6oJfrgUgMAyrC04JIQbzvuOF0sRHHUY4C6VQKmxUJFU0Lj+RRPj1pR6ezk9w==", "license": "MIT OR Apache-2.0", "dependencies": { "@libp2p/interface": "2.10.4", "@noble/hashes": "^1.7.1", - "@waku/proto": "0.0.14-eb93360.0", - "@waku/utils": "0.0.27-eb93360.0", + "@waku/proto": "0.0.15-79dfb35.0", + "@waku/utils": "0.0.28-79dfb35.0", "chai": "^5.1.2", "lodash": "^4.17.21" }, @@ -5534,13 +5866,13 @@ } }, "node_modules/@waku/utils": { - "version": "0.0.27-eb93360.0", - "resolved": "https://registry.npmjs.org/@waku/utils/-/utils-0.0.27-eb93360.0.tgz", - "integrity": "sha512-31vyI/42ZXTHA3PqmIMFFyO2xykDDWTe6SdFALBN66mtMfHKxHY1u2sQ9UzIHK0qVGCAWcEPyAusRXr4FjbTgg==", + "version": "0.0.28-79dfb35.0", + "resolved": "https://registry.npmjs.org/@waku/utils/-/utils-0.0.28-79dfb35.0.tgz", + "integrity": "sha512-wwNg+qGI5MmoT+DD+1+AUM6SMJvOgqcXawVVlXc83UMgWgU44YshBdfrhsxv/CPUfIN2l6i39uU6zhDN5INT4A==", "license": "MIT OR Apache-2.0", "dependencies": { "@noble/hashes": "^1.3.2", - "@waku/interfaces": "0.0.34-eb93360.0", + "@waku/interfaces": "0.0.35-79dfb35.0", "chai": "^4.3.10", "debug": "^4.3.4", "uint8arrays": "^5.0.1" @@ -5754,17 +6086,17 @@ } }, "node_modules/@walletconnect/core/node_modules/unstorage": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.16.1.tgz", - "integrity": "sha512-gdpZ3guLDhz+zWIlYP1UwQ259tG5T5vYRzDaHMkQ1bBY1SQPutvZnrRjTFaWUUpseErJIgAZS51h6NOcZVZiqQ==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.1.tgz", + "integrity": "sha512-KKGwRTT0iVBCErKemkJCLs7JdxNVfqTPc/85ae1XES0+bsHbc/sFBfVi5kJp156cc51BHinIH2l3k0EZ24vOBQ==", "license": "MIT", "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.5", - "h3": "^1.15.3", + "h3": "^1.15.4", "lru-cache": "^10.4.3", - "node-fetch-native": "^1.6.6", + "node-fetch-native": "^1.6.7", "ofetch": "^1.4.1", "ufo": "^1.6.1" }, @@ -5781,6 +6113,7 @@ "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", @@ -5825,6 +6158,9 @@ "@vercel/blob": { "optional": true }, + "@vercel/functions": { + "optional": true + }, "@vercel/kv": { "optional": true }, @@ -6525,6 +6861,27 @@ "events": "3.3.0" } }, + "node_modules/@walletconnect/ethereum-provider/node_modules/abitype": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", + "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/@walletconnect/ethereum-provider/node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -6540,12 +6897,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@walletconnect/ethereum-provider/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, "node_modules/@walletconnect/ethereum-provider/node_modules/isows": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz", @@ -6625,17 +6976,17 @@ } }, "node_modules/@walletconnect/ethereum-provider/node_modules/unstorage": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.16.1.tgz", - "integrity": "sha512-gdpZ3guLDhz+zWIlYP1UwQ259tG5T5vYRzDaHMkQ1bBY1SQPutvZnrRjTFaWUUpseErJIgAZS51h6NOcZVZiqQ==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.1.tgz", + "integrity": "sha512-KKGwRTT0iVBCErKemkJCLs7JdxNVfqTPc/85ae1XES0+bsHbc/sFBfVi5kJp156cc51BHinIH2l3k0EZ24vOBQ==", "license": "MIT", "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.5", - "h3": "^1.15.3", + "h3": "^1.15.4", "lru-cache": "^10.4.3", - "node-fetch-native": "^1.6.6", + "node-fetch-native": "^1.6.7", "ofetch": "^1.4.1", "ufo": "^1.6.1" }, @@ -6652,6 +7003,7 @@ "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", @@ -6696,6 +7048,9 @@ "@vercel/blob": { "optional": true }, + "@vercel/functions": { + "optional": true + }, "@vercel/kv": { "optional": true }, @@ -7083,17 +7438,17 @@ } }, "node_modules/@walletconnect/types/node_modules/unstorage": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.16.1.tgz", - "integrity": "sha512-gdpZ3guLDhz+zWIlYP1UwQ259tG5T5vYRzDaHMkQ1bBY1SQPutvZnrRjTFaWUUpseErJIgAZS51h6NOcZVZiqQ==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.1.tgz", + "integrity": "sha512-KKGwRTT0iVBCErKemkJCLs7JdxNVfqTPc/85ae1XES0+bsHbc/sFBfVi5kJp156cc51BHinIH2l3k0EZ24vOBQ==", "license": "MIT", "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.5", - "h3": "^1.15.3", + "h3": "^1.15.4", "lru-cache": "^10.4.3", - "node-fetch-native": "^1.6.6", + "node-fetch-native": "^1.6.7", "ofetch": "^1.4.1", "ufo": "^1.6.1" }, @@ -7110,6 +7465,7 @@ "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", @@ -7154,6 +7510,9 @@ "@vercel/blob": { "optional": true }, + "@vercel/functions": { + "optional": true + }, "@vercel/kv": { "optional": true }, @@ -7175,9 +7534,9 @@ } }, "node_modules/@walletconnect/universal-provider": { - "version": "2.21.5", - "resolved": "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.21.5.tgz", - "integrity": "sha512-SMXGGXyj78c8Ru2f665ZFZU24phn0yZyCP5Ej7goxVQxABwqWKM/odj3j/IxZv+hxA8yU13yxaubgVefnereqw==", + "version": "2.21.7", + "resolved": "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.21.7.tgz", + "integrity": "sha512-8PB+vA5VuR9PBqt5Y0xj4JC2doYNPlXLGQt3wJORVF9QC227Mm/8R1CAKpmneeLrUH02LkSRwx+wnN/pPnDiQA==", "license": "Apache-2.0", "dependencies": { "@walletconnect/events": "1.0.1", @@ -7187,13 +7546,28 @@ "@walletconnect/jsonrpc-utils": "1.0.8", "@walletconnect/keyvaluestorage": "1.1.1", "@walletconnect/logger": "2.1.2", - "@walletconnect/sign-client": "2.21.5", - "@walletconnect/types": "2.21.5", - "@walletconnect/utils": "2.21.5", + "@walletconnect/sign-client": "2.21.7", + "@walletconnect/types": "2.21.7", + "@walletconnect/utils": "2.21.7", "es-toolkit": "1.39.3", "events": "3.3.0" } }, + "node_modules/@walletconnect/universal-provider/node_modules/@noble/curves": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.2.tgz", + "integrity": "sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@walletconnect/universal-provider/node_modules/@scure/bip32": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", @@ -7222,9 +7596,9 @@ } }, "node_modules/@walletconnect/universal-provider/node_modules/@walletconnect/core": { - "version": "2.21.5", - "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.21.5.tgz", - "integrity": "sha512-CxGbio1TdCkou/TYn8X6Ih1mUX3UtFTk+t618/cIrT3VX5IjQW09n9I/pVafr7bQbBtm9/ATr7ugUEMrLu5snA==", + "version": "2.21.7", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.21.7.tgz", + "integrity": "sha512-q/Au5Ne3g4R+q4GvHR5cvRd3+ha00QZCZiCs058lmy+eDbiZd0YsautvTPJ5a2guD6UaS1k/w5e1JHgixdcgLA==", "license": "Apache-2.0", "dependencies": { "@walletconnect/heartbeat": "1.2.2", @@ -7238,8 +7612,8 @@ "@walletconnect/relay-auth": "1.1.0", "@walletconnect/safe-json": "1.0.2", "@walletconnect/time": "1.0.2", - "@walletconnect/types": "2.21.5", - "@walletconnect/utils": "2.21.5", + "@walletconnect/types": "2.21.7", + "@walletconnect/utils": "2.21.7", "@walletconnect/window-getters": "1.0.1", "es-toolkit": "1.39.3", "events": "3.3.0", @@ -7269,26 +7643,26 @@ } }, "node_modules/@walletconnect/universal-provider/node_modules/@walletconnect/sign-client": { - "version": "2.21.5", - "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.21.5.tgz", - "integrity": "sha512-IAs/IqmE1HVL9EsvqkNRU4NeAYe//h9NwqKi7ToKYZv4jhcC3BBemUD1r8iQJSTHMhO41EKn1G9/DiBln3ZiwQ==", + "version": "2.21.7", + "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.21.7.tgz", + "integrity": "sha512-9k/JEl9copR6nXRhqnmzWz2Zk1hiWysH+o6bp6Cqo8TgDUrZoMLBZMZ6qbo+2HLI54V02kKf0Vg8M81nNFOpjQ==", "license": "Apache-2.0", "dependencies": { - "@walletconnect/core": "2.21.5", + "@walletconnect/core": "2.21.7", "@walletconnect/events": "1.0.1", "@walletconnect/heartbeat": "1.2.2", "@walletconnect/jsonrpc-utils": "1.0.8", "@walletconnect/logger": "2.1.2", "@walletconnect/time": "1.0.2", - "@walletconnect/types": "2.21.5", - "@walletconnect/utils": "2.21.5", + "@walletconnect/types": "2.21.7", + "@walletconnect/utils": "2.21.7", "events": "3.3.0" } }, "node_modules/@walletconnect/universal-provider/node_modules/@walletconnect/types": { - "version": "2.21.5", - "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.21.5.tgz", - "integrity": "sha512-kpTXbenKeMdaz6mgMN/jKaHHbu6mdY3kyyrddzE/mthOd2KLACVrZr7hrTf+Fg2coPVen5d1KKyQjyECEdzOCw==", + "version": "2.21.7", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.21.7.tgz", + "integrity": "sha512-kyGnFje4Iq+XGkZZcSoAIrJWBE4BeghVW4O7n9e1MhUyeOOtO55M/kcqceNGYrvwjHvdN+Kf+aoLnKC0zKlpbQ==", "license": "Apache-2.0", "dependencies": { "@walletconnect/events": "1.0.1", @@ -7300,9 +7674,9 @@ } }, "node_modules/@walletconnect/universal-provider/node_modules/@walletconnect/utils": { - "version": "2.21.5", - "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.21.5.tgz", - "integrity": "sha512-RSPSxPvGMuvfGhd5au1cf9cmHB/KVVLFotJR9ltisjFABGtH2215U5oaVp+a7W18QX37aemejRkvacqOELVySA==", + "version": "2.21.7", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.21.7.tgz", + "integrity": "sha512-qyaclTgcFf9AwVuoV8CLLg8wfH3nX7yZdpylNkDqCpS7wawQL9zmFFTaGgma8sQrCsd3Sd9jUIymcpRvCJnSTw==", "license": "Apache-2.0", "dependencies": { "@msgpack/msgpack": "3.1.2", @@ -7316,7 +7690,7 @@ "@walletconnect/relay-auth": "1.1.0", "@walletconnect/safe-json": "1.0.2", "@walletconnect/time": "1.0.2", - "@walletconnect/types": "2.21.5", + "@walletconnect/types": "2.21.7", "@walletconnect/window-getters": "1.0.1", "@walletconnect/window-metadata": "1.0.1", "blakejs": "1.2.1", @@ -7327,6 +7701,27 @@ "viem": "2.31.0" } }, + "node_modules/@walletconnect/universal-provider/node_modules/abitype": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", + "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/@walletconnect/universal-provider/node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -7352,12 +7747,6 @@ "benchmarks" ] }, - "node_modules/@walletconnect/universal-provider/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, "node_modules/@walletconnect/universal-provider/node_modules/multiformats": { "version": "9.9.0", "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", @@ -7417,17 +7806,17 @@ } }, "node_modules/@walletconnect/universal-provider/node_modules/unstorage": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.16.1.tgz", - "integrity": "sha512-gdpZ3guLDhz+zWIlYP1UwQ259tG5T5vYRzDaHMkQ1bBY1SQPutvZnrRjTFaWUUpseErJIgAZS51h6NOcZVZiqQ==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.1.tgz", + "integrity": "sha512-KKGwRTT0iVBCErKemkJCLs7JdxNVfqTPc/85ae1XES0+bsHbc/sFBfVi5kJp156cc51BHinIH2l3k0EZ24vOBQ==", "license": "MIT", "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.5", - "h3": "^1.15.3", + "h3": "^1.15.4", "lru-cache": "^10.4.3", - "node-fetch-native": "^1.6.6", + "node-fetch-native": "^1.6.7", "ofetch": "^1.4.1", "ufo": "^1.6.1" }, @@ -7444,6 +7833,7 @@ "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", @@ -7488,6 +7878,9 @@ "@vercel/blob": { "optional": true }, + "@vercel/functions": { + "optional": true + }, "@vercel/kv": { "optional": true }, @@ -7684,6 +8077,27 @@ } } }, + "node_modules/@walletconnect/utils/node_modules/abitype": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", + "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/@walletconnect/utils/node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -7699,12 +8113,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@walletconnect/utils/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, "node_modules/@walletconnect/utils/node_modules/isows": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz", @@ -7778,17 +8186,17 @@ } }, "node_modules/@walletconnect/utils/node_modules/unstorage": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.16.1.tgz", - "integrity": "sha512-gdpZ3guLDhz+zWIlYP1UwQ259tG5T5vYRzDaHMkQ1bBY1SQPutvZnrRjTFaWUUpseErJIgAZS51h6NOcZVZiqQ==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.1.tgz", + "integrity": "sha512-KKGwRTT0iVBCErKemkJCLs7JdxNVfqTPc/85ae1XES0+bsHbc/sFBfVi5kJp156cc51BHinIH2l3k0EZ24vOBQ==", "license": "MIT", "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.5", - "h3": "^1.15.3", + "h3": "^1.15.4", "lru-cache": "^10.4.3", - "node-fetch-native": "^1.6.6", + "node-fetch-native": "^1.6.7", "ofetch": "^1.4.1", "ufo": "^1.6.1" }, @@ -7805,6 +8213,7 @@ "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", @@ -7849,6 +8258,9 @@ "@vercel/blob": { "optional": true }, + "@vercel/functions": { + "optional": true + }, "@vercel/kv": { "optional": true }, @@ -7952,16 +8364,16 @@ "license": "0BSD" }, "node_modules/abitype": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", - "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.1.0.tgz", + "integrity": "sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/wevm" }, "peerDependencies": { "typescript": ">=5.0.4", - "zod": "^3 >=3.22.0" + "zod": "^3.22.0 || ^4.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -7979,9 +8391,9 @@ "license": "Apache-2.0 OR MIT" }, "node_modules/acorn": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", - "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -8002,9 +8414,9 @@ } }, "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", "engines": { @@ -8029,15 +8441,12 @@ } }, "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=8" } }, "node_modules/ansi-styles": { @@ -8098,9 +8507,9 @@ "license": "Python-2.0" }, "node_modules/aria-hidden": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", - "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", "license": "MIT", "dependencies": { "tslib": "^2.0.0" @@ -8119,6 +8528,16 @@ "dequal": "^2.0.3" } }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -8153,9 +8572,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.20", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", - "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", "dev": true, "funding": [ { @@ -8173,11 +8592,11 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.23.3", - "caniuse-lite": "^1.0.30001646", + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.1", + "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -8267,6 +8686,16 @@ ], "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.5.tgz", + "integrity": "sha512-TiU4qUT9jdCuh4aVOG7H1QozyeI2sZRqoRPdqBIaslfNt4WUSanRBueAwl2x5jt4rXBMim3lIN2x6yT8PDi24Q==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/bech32": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", @@ -8298,16 +8727,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, "node_modules/bip174": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bip174/-/bip174-2.1.1.tgz", @@ -8317,32 +8736,6 @@ "node": ">=8.0.0" } }, - "node_modules/bip322-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bip322-js/-/bip322-js-2.0.0.tgz", - "integrity": "sha512-wyewxyCLl+wudZWiyvA46SaNQL41dVDJ+sx4HvD6zRXScHzAycwuKEMmbvr2qN+P/IIYArF4XVqlyZVnjutELQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "@bitcoinerlab/secp256k1": "^1.1.1", - "bitcoinjs-lib": "^6.1.5", - "bitcoinjs-message": "^2.2.0", - "ecpair": "^2.1.0", - "elliptic": "^6.5.5", - "fast-sha256": "^1.3.0", - "secp256k1": "^5.0.0" - } - }, - "node_modules/bip66": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", - "integrity": "sha512-nemMHz95EmS38a26XbbdxIYj5csHd3RMP3H5bwQknX0WYHF01qhpufP42mLOwVICuH2JmhIhXiWs89MfUGL7Xw==", - "license": "MIT", - "optional": true, - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, "node_modules/bitcoin-address-validation": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/bitcoin-address-validation/-/bitcoin-address-validation-2.2.3.tgz", @@ -8371,91 +8764,6 @@ "node": ">=8.0.0" } }, - "node_modules/bitcoinjs-message": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/bitcoinjs-message/-/bitcoinjs-message-2.2.0.tgz", - "integrity": "sha512-103Wy3xg8Y9o+pdhGP4M3/mtQQuUWs6sPuOp1mYphSUoSMHjHTlkj32K4zxU8qMH0Ckv23emfkGlFWtoWZ7YFA==", - "license": "MIT", - "optional": true, - "dependencies": { - "bech32": "^1.1.3", - "bs58check": "^2.1.2", - "buffer-equals": "^1.0.3", - "create-hash": "^1.1.2", - "secp256k1": "^3.0.1", - "varuint-bitcoin": "^1.0.1" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/bitcoinjs-message/node_modules/base-x": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz", - "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==", - "license": "MIT", - "optional": true, - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/bitcoinjs-message/node_modules/bech32": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", - "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", - "license": "MIT", - "optional": true - }, - "node_modules/bitcoinjs-message/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", - "license": "MIT", - "optional": true - }, - "node_modules/bitcoinjs-message/node_modules/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", - "license": "MIT", - "optional": true, - "dependencies": { - "base-x": "^3.0.2" - } - }, - "node_modules/bitcoinjs-message/node_modules/bs58check": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", - "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", - "license": "MIT", - "optional": true, - "dependencies": { - "bs58": "^4.0.0", - "create-hash": "^1.1.0", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/bitcoinjs-message/node_modules/secp256k1": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.8.1.tgz", - "integrity": "sha512-tArjQw2P0RTdY7QmkNehgp6TVvQXq6ulIhxv8gaH6YubKG/wxxAoNKcbuXjDhybbc+b2Ihc7e0xxiGN744UIiQ==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "bindings": "^1.5.0", - "bip66": "^1.1.5", - "bn.js": "^4.11.8", - "create-hash": "^1.2.0", - "drbg.js": "^1.0.1", - "elliptic": "^6.5.7", - "nan": "^2.14.0", - "safe-buffer": "^5.1.2" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/blakejs": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", @@ -8469,15 +8777,15 @@ "license": "MIT" }, "node_modules/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.12.1.tgz", + "integrity": "sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==", "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -8497,32 +8805,10 @@ "node": ">=8" } }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "license": "MIT", - "optional": true - }, - "node_modules/browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "license": "MIT", - "optional": true, - "dependencies": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", "dev": true, "funding": [ { @@ -8540,10 +8826,11 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.1" + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -8610,23 +8897,6 @@ "ieee754": "^1.2.1" } }, - "node_modules/buffer-equals": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/buffer-equals/-/buffer-equals-1.0.4.tgz", - "integrity": "sha512-99MsCq0j5+RhubVEtKQgKaD6EM+UP3xJgIvQqwJ3SOLDUekzxMX1ylXBng+Wa2sh7mGT0W6RUly8ojjr1Tt6nA==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "license": "MIT", - "optional": true - }, "node_modules/bufferutil": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz", @@ -8726,9 +8996,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001741", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001741.tgz", - "integrity": "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==", + "version": "1.0.30001743", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", + "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", "dev": true, "funding": [ { @@ -8773,12 +9043,6 @@ "node": ">=6" } }, - "node_modules/cbw-sdk/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, "node_modules/ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", @@ -8907,24 +9171,11 @@ "node": ">= 6" } }, - "node_modules/cipher-base": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz", - "integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==", - "license": "MIT", - "optional": true, - "dependencies": { - "inherits": "^2.0.4", - "safe-buffer": "^5.2.1" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", "dependencies": { "clsx": "^2.1.1" }, @@ -8943,15 +9194,6 @@ "wrap-ansi": "^6.2.0" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/cliui/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -8972,18 +9214,6 @@ "node": ">=8" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cliui/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -9008,381 +9238,19 @@ } }, "node_modules/cmdk": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.0.0.tgz", - "integrity": "sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", "license": "MIT", "dependencies": { - "@radix-ui/react-dialog": "1.0.5", - "@radix-ui/react-primitive": "1.0.3" + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/cmdk/node_modules/@radix-ui/primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", - "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-compose-refs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", - "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-context": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", - "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-dialog": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", - "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.1", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", - "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-escape-keydown": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-focus-guards": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", - "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-focus-scope": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", - "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-id": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", - "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-portal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", - "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-presence": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", - "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-primitive": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", - "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-slot": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", - "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", - "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", - "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", - "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", - "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/react-remove-scroll": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", - "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", - "license": "MIT", - "dependencies": { - "react-remove-scroll-bar": "^2.3.3", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "node_modules/color-convert": { @@ -9465,35 +9333,6 @@ "node": ">=0.8" } }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "license": "MIT", - "optional": true, - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "license": "MIT", - "optional": true, - "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, "node_modules/cross-fetch": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", @@ -9507,6 +9346,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -9734,9 +9574,9 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -9760,9 +9600,9 @@ } }, "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "dev": true, "license": "MIT" }, @@ -9915,6 +9755,19 @@ "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", "license": "MIT" }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -9943,6 +9796,19 @@ "node": ">=6" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dom-accessibility-api": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", @@ -9961,21 +9827,6 @@ "csstype": "^3.0.2" } }, - "node_modules/drbg.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz", - "integrity": "sha512-F4wZ06PvqxYLFEZKkFxTDcns9oFNk34hvmJSEwdzsxVQ8YI5YaxtACgQatkYgv2VI2CFkUd2Y+xosPQnHv809g==", - "license": "MIT", - "optional": true, - "dependencies": { - "browserify-aes": "^1.0.6", - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -10025,106 +9876,39 @@ "node": ">=16" } }, - "node_modules/eciesjs/node_modules/@ecies/ciphers": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.4.tgz", - "integrity": "sha512-t+iX+Wf5nRKyNzk8dviW3Ikb/280+aEJAnw9YXvCp2tYGPSkMki+NRY+8aNLmVFv3eNtMdvViPNOPxS8SZNP+w==", - "license": "MIT", - "engines": { - "bun": ">=1", - "deno": ">=2", - "node": ">=16" - }, - "peerDependencies": { - "@noble/ciphers": "^1.0.0" - } - }, - "node_modules/eciesjs/node_modules/@noble/curves": { - "version": "1.9.6", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.6.tgz", - "integrity": "sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.8.0" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/ecpair": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ecpair/-/ecpair-2.1.0.tgz", - "integrity": "sha512-cL/mh3MtJutFOvFc27GPZE2pWL3a3k4YvzUWEOvilnfZVlH3Jwgx/7d6tlD7/75tNk8TG2m+7Kgtz0SI1tWcqw==", - "license": "MIT", - "optional": true, - "dependencies": { - "randombytes": "^2.1.0", - "typeforce": "^1.18.0", - "wif": "^2.0.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/electron-to-chromium": { - "version": "1.5.45", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.45.tgz", - "integrity": "sha512-vOzZS6uZwhhbkZbcRyiy99Wg+pYFV5hk+5YaECvx0+Z31NR3Tt5zS6dze2OepT6PCTzVzT0dIJItti+uAW5zmw==", + "version": "1.5.221", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.221.tgz", + "integrity": "sha512-/1hFJ39wkW01ogqSyYoA4goOXOtMRy6B+yvA1u42nnsEGtHzIzmk93aPISumVQeblj47JUHLC9coCjUxb1EvtQ==", "dev": true, "license": "ISC" }, - "node_modules/elliptic": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", - "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", - "license": "MIT", - "optional": true, - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", - "license": "MIT", - "optional": true - }, "node_modules/embla-carousel": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.3.0.tgz", - "integrity": "sha512-Ve8dhI4w28qBqR8J+aMtv7rLK89r1ZA5HocwFz6uMB/i5EiC7bGI7y+AM80yAVUJw3qqaZYK7clmZMUR8kM3UA==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", + "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", "license": "MIT" }, "node_modules/embla-carousel-react": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.3.0.tgz", - "integrity": "sha512-P1FlinFDcIvggcErRjNuVqnUR8anyo8vLMIH8Rthgofw7Nj8qTguCa2QjFAbzxAUTQTPNNjNL7yt0BGGinVdFw==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.6.0.tgz", + "integrity": "sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==", "license": "MIT", "dependencies": { - "embla-carousel": "8.3.0", - "embla-carousel-reactive-utils": "8.3.0" + "embla-carousel": "8.6.0", + "embla-carousel-reactive-utils": "8.6.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.1 || ^18.0.0" + "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "node_modules/embla-carousel-reactive-utils": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.3.0.tgz", - "integrity": "sha512-EYdhhJ302SC4Lmkx8GRsp0sjUhEN4WyFXPOk0kGu9OXZSRMmcBlRgTvHcq8eKJE1bXWBsOi1T83B+BSSVZSmwQ==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.6.0.tgz", + "integrity": "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==", "license": "MIT", "peerDependencies": { - "embla-carousel": "8.3.0" + "embla-carousel": "8.6.0" } }, "node_modules/emoji-regex": { @@ -10284,9 +10068,9 @@ ] }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -10294,32 +10078,35 @@ "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" } }, "node_modules/escalade": { @@ -10346,70 +10133,66 @@ } }, "node_modules/eslint": { - "version": "9.13.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz", - "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.18.0", - "@eslint/core": "^0.7.0", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.13.0", - "@eslint/plugin-kit": "^0.2.0", - "@humanfs/node": "^0.16.5", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.1", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", + "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.1.0", - "eslint-visitor-keys": "^4.1.0", - "espree": "^10.2.0", - "esquery": "^1.5.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", + "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-plugin-react-hooks": { - "version": "5.1.0-rc-fb9a90fa48-20240614", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0-rc-fb9a90fa48-20240614.tgz", - "integrity": "sha512-xsiRwaDNF5wWNC4ZHLut+x/YcAxksUd9Rizt7LaEn3bV8VyYRpXnRJQlLOfYaVy9esk4DFP4zPPnoNVjq5Gc0w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "dev": true, "license": "MIT", "engines": { @@ -10420,19 +10203,19 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.14.tgz", - "integrity": "sha512-aXvzCTK7ZBv1e7fahFuR3Z/fyQQSIQ711yPgYRj+Oj64tyTgO4iQIDmYXDBqvSWQ/FA4OSCsXOStlF+noU0/NA==", + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", "dev": true, "license": "MIT", "peerDependencies": { - "eslint": ">=7" + "eslint": ">=8.40" } }, "node_modules/eslint-scope": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", - "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -10447,9 +10230,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", - "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -10459,16 +10242,142 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/espree": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", - "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", + "node_modules/eslint/node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.12.0", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.1.0" + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/eslint/node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10575,15 +10484,6 @@ "node": ">=14.0.0" } }, - "node_modules/eth-block-tracker/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/eth-json-rpc-filters": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/eth-json-rpc-filters/-/eth-json-rpc-filters-6.0.1.tgz", @@ -10680,9 +10580,9 @@ "license": "MIT" }, "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "license": "MIT" }, "node_modules/events": { @@ -10694,21 +10594,10 @@ "node": ">=0.8.x" } }, - "node_modules/evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "license": "MIT", - "optional": true, - "dependencies": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, "node_modules/expect-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", - "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -10741,25 +10630,25 @@ "license": "MIT" }, "node_modules/fast-equals": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", - "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz", + "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==", "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -10806,17 +10695,10 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", "license": "MIT" }, - "node_modules/fast-sha256": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", - "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", - "license": "Unlicense", - "optional": true - }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -10842,13 +10724,6 @@ "node": ">=16.0.0" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT", - "optional": true - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -10944,12 +10819,12 @@ } }, "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -10989,6 +10864,13 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -11082,6 +10964,19 @@ "node": ">= 0.4" } }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -11115,9 +11010,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -11139,9 +11034,9 @@ } }, "node_modules/globals": { - "version": "15.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.11.0.tgz", - "integrity": "sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==", + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, "license": "MIT", "engines": { @@ -11151,6 +11046,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -11236,32 +11152,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "license": "MIT", - "optional": true, - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "license": "MIT", - "optional": true, - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, "node_modules/hashlru": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/hashlru/-/hashlru-2.3.0.tgz", @@ -11341,18 +11231,6 @@ "integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==", "license": "MIT" }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "license": "MIT", - "optional": true, - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", @@ -11454,9 +11332,9 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11490,6 +11368,18 @@ "node": ">=8" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -11503,13 +11393,13 @@ "license": "MIT" }, "node_modules/input-otp": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.2.4.tgz", - "integrity": "sha512-md6rhmD+zmMnUh5crQNSQxq3keBRYvE3odbr4Qb9g2NWzQv9azi+t1a3X4TBTbh98fsGHgEEJlzbe1q860uGCA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.4.2.tgz", + "integrity": "sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==", "license": "MIT", "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "node_modules/interface-datastore": { @@ -11537,15 +11427,6 @@ "node": ">=12" } }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/iron-webcrypto": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", @@ -11620,9 +11501,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -11715,9 +11596,9 @@ "license": "MIT" }, "node_modules/is-network-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", - "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.0.tgz", + "integrity": "sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==", "license": "MIT", "engines": { "node": ">=16" @@ -11735,6 +11616,16 @@ "node": ">=0.12.0" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", @@ -12074,9 +11965,9 @@ } }, "node_modules/jiti": { - "version": "1.21.6", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", - "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", "bin": { "jiti": "bin/jiti.js" @@ -12360,13 +12251,21 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -12413,6 +12312,7 @@ "version": "0.462.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.462.0.tgz", "integrity": "sha512-NTL7EbAao9IFtuSivSZgrAh4fZd09Lr+6MTkqIxuHaH2nnYiYIzXPo06cOxHg9wKLdj6LL8TByG4qpePqwgx/g==", + "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } @@ -12429,13 +12329,13 @@ } }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/main-event": { @@ -12463,18 +12363,6 @@ "node": ">= 0.4" } }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "license": "MIT", - "optional": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, "node_modules/mdast-util-find-and-replace": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", @@ -13379,20 +13267,6 @@ "node": ">=4" } }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "license": "ISC", - "optional": true - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "license": "MIT", - "optional": true - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -13463,9 +13337,9 @@ "license": "MIT" }, "node_modules/multiformats": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.4.0.tgz", - "integrity": "sha512-Mkb/QcclrJxKC+vrcIFl297h52QcKh2Az/9A5vbWytbQt4225UWWWmIuSsKksdww9NkIeYcA7DkfftyLuC/JSg==", + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.4.1.tgz", + "integrity": "sha512-VqO6OSvLrFVAYYjgsr8tyv62/rCQhPgsZUXLTqoFLSgdkgiUYKYeArbt1uWLlEpkjxQe+P0+sHlbPEte1Bi06Q==", "license": "Apache-2.0 OR MIT" }, "node_modules/mz": { @@ -13479,17 +13353,10 @@ "thenify-all": "^1.0.0" } }, - "node_modules/nan": { - "version": "2.23.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz", - "integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==", - "license": "MIT", - "optional": true - }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", @@ -13596,15 +13463,15 @@ } }, "node_modules/node-mock-http": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.2.tgz", - "integrity": "sha512-zWaamgDUdo9SSLw47we78+zYw/bDr5gH8pH7oRRs8V3KmBtu8GLgGIbV2p/gRPd3LWpEOpjQj7X1FOU3VFMJ8g==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.3.tgz", + "integrity": "sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog==", "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", "dev": true, "license": "MIT" }, @@ -13628,9 +13495,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", - "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", + "version": "2.2.22", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz", + "integrity": "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==", "dev": true, "license": "MIT" }, @@ -13725,6 +13592,10 @@ "wrappy": "1" } }, + "node_modules/opchan": { + "resolved": "app", + "link": true + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -13811,12 +13682,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/ox/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, "node_modules/p-defer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-4.0.1.tgz", @@ -13877,9 +13742,9 @@ } }, "node_modules/p-queue": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.0.tgz", - "integrity": "sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.1.tgz", + "integrity": "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==", "license": "MIT", "dependencies": { "eventemitter3": "^5.0.1", @@ -13892,12 +13757,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-queue/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, "node_modules/p-retry": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", @@ -14002,6 +13861,16 @@ "node": ">=8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -14033,6 +13902,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -14068,12 +13947,12 @@ } }, "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, "node_modules/pino": { @@ -14115,9 +13994,9 @@ "license": "MIT" }, "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "license": "MIT", "engines": { "node": ">= 6" @@ -14151,9 +14030,9 @@ } }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -14170,8 +14049,8 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -14196,9 +14075,19 @@ } }, "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { "camelcase-css": "^2.0.1" @@ -14206,10 +14095,6 @@ "engines": { "node": "^12 || ^14 || >= 16" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": "^8.4.21" } @@ -14274,7 +14159,7 @@ "postcss": "^8.2.14" } }, - "node_modules/postcss-selector-parser": { + "node_modules/postcss-nested/node_modules/postcss-selector-parser": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", @@ -14287,6 +14172,20 @@ "node": ">=4" } }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", @@ -14345,17 +14244,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/pretty-format/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", @@ -14370,14 +14258,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -14529,9 +14409,9 @@ "license": "MIT" }, "node_modules/quick-lru": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-7.1.0.tgz", - "integrity": "sha512-Pzd/4IFnTb8E+I1P5rbLQoqpUHcXKg48qTYKi4EANg+sTPwGFEMOcYGiiZz6xuQcOMZP7MPsrdAPx+16Q8qahg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-7.2.0.tgz", + "integrity": "sha512-fG4L8TlD1CacJiGMGPxM1/K8l/GaKL2eFQZ6DWAjxZYxSf07DkumbC/Mhh+u/NHvxkfQVL25By0pxBS8QE9ZrQ==", "license": "MIT", "engines": { "node": ">=18" @@ -14561,16 +14441,6 @@ "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", "license": "MIT" }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/re-resizable": { "version": "6.11.2", "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.11.2.tgz", @@ -14621,9 +14491,9 @@ } }, "node_modules/react-hook-form": { - "version": "7.53.1", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.53.1.tgz", - "integrity": "sha512-6aiQeBda4zjcuaugWvim9WsGqisoUk+etmFEsSUMm451/Ic8L/UAb7sRtMj3V+Hdzm6mMjU1VhiSzYUZeBm0Vg==", + "version": "7.62.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz", + "integrity": "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -14637,10 +14507,12 @@ } }, "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true }, "node_modules/react-markdown": { "version": "10.1.0", @@ -14670,23 +14542,23 @@ } }, "node_modules/react-remove-scroll": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz", - "integrity": "sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", "license": "MIT", "dependencies": { - "react-remove-scroll-bar": "^2.3.6", - "react-style-singleton": "^2.2.1", + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" }, "engines": { "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -14695,20 +14567,20 @@ } }, "node_modules/react-remove-scroll-bar": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", - "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", "license": "MIT", "dependencies": { - "react-style-singleton": "^2.2.1", + "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "engines": { "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -14717,9 +14589,9 @@ } }, "node_modules/react-resizable-panels": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.1.5.tgz", - "integrity": "sha512-JMSe18rYupmx+dzYcdfWYZ93ZdxqQmLum3xWDVSUMI0UVwl9bB9gUaFmPbxYoO4G+m5sqgdXQCYQxnOysytfnw==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.1.9.tgz", + "integrity": "sha512-z77+X08YDIrgAes4jl8xhnUu1LNIRp4+E7cv4xHmLOxxUPO/ML7PSrE813b90vj7xvQ1lcf7g2uA9GeMZonjhQ==", "license": "MIT", "peerDependencies": { "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", @@ -14727,12 +14599,12 @@ } }, "node_modules/react-router": { - "version": "6.27.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz", - "integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==", + "version": "6.30.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz", + "integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.20.0" + "@remix-run/router": "1.23.0" }, "engines": { "node": ">=14.0.0" @@ -14742,13 +14614,13 @@ } }, "node_modules/react-router-dom": { - "version": "6.27.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz", - "integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==", + "version": "6.30.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz", + "integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.20.0", - "react-router": "6.27.0" + "@remix-run/router": "1.23.0", + "react-router": "6.30.1" }, "engines": { "node": ">=14.0.0" @@ -14759,9 +14631,9 @@ } }, "node_modules/react-smooth": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz", - "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", "license": "MIT", "dependencies": { "fast-equals": "^5.0.1", @@ -14769,26 +14641,25 @@ "react-transition-group": "^4.4.5" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/react-style-singleton": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", - "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", "license": "MIT", "dependencies": { "get-nonce": "^1.0.0", - "invariant": "^2.2.4", "tslib": "^2.0.0" }, "engines": { "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -14821,6 +14692,15 @@ "pify": "^2.3.0" } }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -14857,16 +14737,16 @@ } }, "node_modules/recharts": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.13.0.tgz", - "integrity": "sha512-sbfxjWQ+oLWSZEWmvbq/DFVdeRLqqA6d0CDjKx2PkxVVdoXo16jvENCE+u/x7HxOO+/fwx//nYRwb8p8X6s/lQ==", + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", + "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", "license": "MIT", "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", "lodash": "^4.17.21", "react-is": "^18.3.1", - "react-smooth": "^4.0.0", + "react-smooth": "^4.0.4", "recharts-scale": "^0.4.4", "tiny-invariant": "^1.3.1", "victory-vendor": "^36.6.8" @@ -14875,8 +14755,8 @@ "node": ">=14" }, "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/recharts-scale": { @@ -14888,6 +14768,18 @@ "decimal.js-light": "^2.4.1" } }, + "node_modules/recharts/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/recharts/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -14998,18 +14890,21 @@ "license": "ISC" }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -15024,6 +14919,16 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -15034,34 +14939,62 @@ } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "license": "MIT", - "optional": true, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rollup": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", - "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.2.tgz", + "integrity": "sha512-BgLRGy7tNS9H66aIMASq1qSYbAAJV6Z6WR4QYTvj5FgF15rZ/ympT1uixHXwzbZUBDbkvqUI1KR0fH1FhMaQ9w==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -15071,22 +15004,27 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.0", - "@rollup/rollup-android-arm64": "4.24.0", - "@rollup/rollup-darwin-arm64": "4.24.0", - "@rollup/rollup-darwin-x64": "4.24.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", - "@rollup/rollup-linux-arm-musleabihf": "4.24.0", - "@rollup/rollup-linux-arm64-gnu": "4.24.0", - "@rollup/rollup-linux-arm64-musl": "4.24.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", - "@rollup/rollup-linux-riscv64-gnu": "4.24.0", - "@rollup/rollup-linux-s390x-gnu": "4.24.0", - "@rollup/rollup-linux-x64-gnu": "4.24.0", - "@rollup/rollup-linux-x64-musl": "4.24.0", - "@rollup/rollup-win32-arm64-msvc": "4.24.0", - "@rollup/rollup-win32-ia32-msvc": "4.24.0", - "@rollup/rollup-win32-x64-msvc": "4.24.0", + "@rollup/rollup-android-arm-eabi": "4.50.2", + "@rollup/rollup-android-arm64": "4.50.2", + "@rollup/rollup-darwin-arm64": "4.50.2", + "@rollup/rollup-darwin-x64": "4.50.2", + "@rollup/rollup-freebsd-arm64": "4.50.2", + "@rollup/rollup-freebsd-x64": "4.50.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.50.2", + "@rollup/rollup-linux-arm-musleabihf": "4.50.2", + "@rollup/rollup-linux-arm64-gnu": "4.50.2", + "@rollup/rollup-linux-arm64-musl": "4.50.2", + "@rollup/rollup-linux-loong64-gnu": "4.50.2", + "@rollup/rollup-linux-ppc64-gnu": "4.50.2", + "@rollup/rollup-linux-riscv64-gnu": "4.50.2", + "@rollup/rollup-linux-riscv64-musl": "4.50.2", + "@rollup/rollup-linux-s390x-gnu": "4.50.2", + "@rollup/rollup-linux-x64-gnu": "4.50.2", + "@rollup/rollup-linux-x64-musl": "4.50.2", + "@rollup/rollup-openharmony-arm64": "4.50.2", + "@rollup/rollup-win32-arm64-msvc": "4.50.2", + "@rollup/rollup-win32-ia32-msvc": "4.50.2", + "@rollup/rollup-win32-x64-msvc": "4.50.2", "fsevents": "~2.3.2" } }, @@ -15206,29 +15144,6 @@ "loose-envify": "^1.1.0" } }, - "node_modules/secp256k1": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.1.tgz", - "integrity": "sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "elliptic": "^6.5.7", - "node-addon-api": "^5.0.0", - "node-gyp-build": "^4.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/secp256k1/node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", - "license": "MIT", - "optional": true - }, "node_modules/semver": { "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", @@ -15331,9 +15246,9 @@ } }, "node_modules/sirv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz", - "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", "dev": true, "license": "MIT", "dependencies": { @@ -15345,6 +15260,16 @@ "node": ">=18" } }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/socket.io-client": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", @@ -15417,13 +15342,13 @@ } }, "node_modules/sonner": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.5.0.tgz", - "integrity": "sha512-FBjhG/gnnbN6FY0jaNnqZOMmB73R+5IiyYAw8yBj7L54ER7HB3fOSE5OFiQiE2iXWxeXKvg6fIP4LtVppHEdJA==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.7.4.tgz", + "integrity": "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==", "license": "MIT", "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "node_modules/source-map-js": { @@ -15533,31 +15458,37 @@ "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/stringify-entities": { @@ -15575,22 +15506,6 @@ } }, "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", @@ -15602,11 +15517,15 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { "node": ">=8" } @@ -15739,9 +15658,9 @@ "license": "MIT" }, "node_modules/tailwind-merge": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.4.tgz", - "integrity": "sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", "license": "MIT", "funding": { "type": "github", @@ -15794,6 +15713,19 @@ "tailwindcss": ">=3.0.0 || insiders" } }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -15852,14 +15784,14 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -15869,11 +15801,14 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -15884,9 +15819,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -16029,9 +15964,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, "license": "MIT", "engines": { @@ -16048,17 +15983,30 @@ "license": "Apache-2.0" }, "node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, - "node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "license": "Unlicense", - "optional": true + "node_modules/tsx": { + "version": "4.20.5", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.5.tgz", + "integrity": "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } }, "node_modules/type-check": { "version": "0.4.0", @@ -16082,6 +16030,19 @@ "node": ">=4" } }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -16103,9 +16064,9 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -16116,15 +16077,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.11.0.tgz", - "integrity": "sha512-cBRGnW3FSlxaYwU8KfAewxFK5uzeOAp0l2KebIlPDOT5olVi65KDG/yjBooPBG0kGW/HLkoz1c/iuBFehcS3IA==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.0.tgz", + "integrity": "sha512-ib7mCkYuIzYonCq9XWF5XNw+fkj2zg629PSa9KNIQ47RXFF763S5BIX4wqz1+FLPogTZoiw8KmCiRPRa8bL3qw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.11.0", - "@typescript-eslint/parser": "8.11.0", - "@typescript-eslint/utils": "8.11.0" + "@typescript-eslint/eslint-plugin": "8.44.0", + "@typescript-eslint/parser": "8.44.0", + "@typescript-eslint/typescript-estree": "8.44.0", + "@typescript-eslint/utils": "8.44.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -16133,10 +16095,241 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.0.tgz", + "integrity": "sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.44.0", + "@typescript-eslint/type-utils": "8.44.0", + "@typescript-eslint/utils": "8.44.0", + "@typescript-eslint/visitor-keys": "8.44.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.44.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.0.tgz", + "integrity": "sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.44.0", + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/typescript-estree": "8.44.0", + "@typescript-eslint/visitor-keys": "8.44.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.0.tgz", + "integrity": "sha512-87Jv3E+al8wpD+rIdVJm/ItDBe/Im09zXIjFoipOjr5gHUhJmTzfFLuTJ/nPTMc2Srsroy4IBXwcTCHyRR7KzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/visitor-keys": "8.44.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.0.tgz", + "integrity": "sha512-9cwsoSxJ8Sak67Be/hD2RNt/fsqmWnNE1iHohG8lxqLSNY8xNfyY7wloo5zpW3Nu9hxVgURevqfcH6vvKCt6yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/typescript-estree": "8.44.0", + "@typescript-eslint/utils": "8.44.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.0.tgz", + "integrity": "sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.0.tgz", + "integrity": "sha512-lqNj6SgnGcQZwL4/SBJ3xdPEfcBuhCG8zdcwCPgYcmiPLgokiNDKlbPzCwEwu7m279J/lBYWtDYL+87OEfn8Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.44.0", + "@typescript-eslint/tsconfig-utils": "8.44.0", + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/visitor-keys": "8.44.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.0.tgz", + "integrity": "sha512-nktOlVcg3ALo0mYlV+L7sWUD58KG4CMj1rb2HUVOO4aL3K/6wcD+NERqd0rrA5Vg06b42YhF6cFxeixsp9Riqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.44.0", + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/typescript-estree": "8.44.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.0.tgz", + "integrity": "sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.44.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/typescript-eslint/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typescript-eslint/node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" } }, "node_modules/ufo": { @@ -16180,9 +16373,9 @@ "license": "MIT" }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, "node_modules/unified": { @@ -16273,9 +16466,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -16294,7 +16487,7 @@ "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -16314,9 +16507,9 @@ } }, "node_modules/use-callback-ref": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", - "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", "license": "MIT", "dependencies": { "tslib": "^2.0.0" @@ -16325,8 +16518,8 @@ "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -16335,9 +16528,9 @@ } }, "node_modules/use-sidecar": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", - "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", "license": "MIT", "dependencies": { "detect-node-es": "^1.1.0", @@ -16347,8 +16540,8 @@ "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -16357,9 +16550,9 @@ } }, "node_modules/use-sync-external-store": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", - "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -16425,9 +16618,9 @@ } }, "node_modules/valtio": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/valtio/-/valtio-2.1.5.tgz", - "integrity": "sha512-vsh1Ixu5mT0pJFZm+Jspvhga5GzHUTYv0/+Th203pLfh3/wbHwxhu/Z2OkZDXIgHfjnjBns7SN9HNcbDvPmaGw==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/valtio/-/valtio-2.1.7.tgz", + "integrity": "sha512-DwJhCDpujuQuKdJ2H84VbTjEJJteaSmqsuUltsfbfdbotVfNeTE4K/qc/Wi57I9x8/2ed4JNdjEna7O6PfavRg==", "license": "MIT", "dependencies": { "proxy-compare": "^3.0.1" @@ -16521,9 +16714,9 @@ } }, "node_modules/viem": { - "version": "2.37.1", - "resolved": "https://registry.npmjs.org/viem/-/viem-2.37.1.tgz", - "integrity": "sha512-IzacdIXYlOvzDJwNKIVa53LP/LaP70qvBGAIoGH6R+n06S/ru/nnQxLNZ6+JImvIcxwNwgAl0jUA6FZEIQQWSw==", + "version": "2.37.6", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.37.6.tgz", + "integrity": "sha512-b+1IozQ8TciVQNdQUkOH5xtFR0z7ZxR8pyloENi/a+RA408lv4LoX12ofwoiT3ip0VRhO5ni1em//X0jn/eW0g==", "funding": [ { "type": "github", @@ -16536,7 +16729,7 @@ "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", - "abitype": "1.0.8", + "abitype": "1.1.0", "isows": "1.0.7", "ox": "0.9.3", "ws": "8.18.3" @@ -16592,12 +16785,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/viem/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, "node_modules/viem/node_modules/ox": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/ox/-/ox-0.9.3.tgz", @@ -16628,31 +16815,10 @@ } } }, - "node_modules/viem/node_modules/ox/node_modules/abitype": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.9.tgz", - "integrity": "sha512-oN0S++TQmlwWuB+rkA6aiEefLv3SP+2l/tC5mux/TLj6qdA6rF15Vbpex4fHovLsMkwLwTIRj8/Q8vXCS3GfOg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/wevm" - }, - "peerDependencies": { - "typescript": ">=5.0.4", - "zod": "^3 >=3.22.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, "node_modules/vite": { - "version": "5.4.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", - "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "version": "5.4.20", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.20.tgz", + "integrity": "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==", "dev": true, "license": "MIT", "dependencies": { @@ -16732,6 +16898,436 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, "node_modules/vitest": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", @@ -16806,9 +17402,9 @@ } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -16832,13 +17428,13 @@ } }, "node_modules/wagmi": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/wagmi/-/wagmi-2.16.1.tgz", - "integrity": "sha512-iUdaoe/xd5NiNRW72QVctZs+962EORJKAvJTCsmf9n6TnEApPlENuvVRJKgobI4cGUgi5scWAstpLprB+RRo9Q==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/wagmi/-/wagmi-2.17.0.tgz", + "integrity": "sha512-NxdS/oHG3j4l3JzNIAb0zL14p3GXOS9YU4Q3WS+0eTChFIfdqBrurQMImPLNC9P5rXYym6cjC2feeqhHVMJjpg==", "license": "MIT", "dependencies": { - "@wagmi/connectors": "5.9.1", - "@wagmi/core": "2.18.1", + "@wagmi/connectors": "5.10.0", + "@wagmi/core": "2.21.0", "use-sync-external-store": "1.4.0" }, "funding": { @@ -16856,6 +17452,15 @@ } } }, + "node_modules/wagmi/node_modules/use-sync-external-store": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/weald": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/weald/-/weald-1.0.6.tgz", @@ -16876,9 +17481,9 @@ } }, "node_modules/weald/node_modules/supports-color": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.0.tgz", - "integrity": "sha512-5eG9FQjEjDbAlI5+kdpdyPIBMRH4GfTVDGREVupaZHmVoppknhM29b/S9BkQz7cathp85BVgRi/As3Siln7e0Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", "license": "MIT", "engines": { "node": ">=18" @@ -17012,48 +17617,6 @@ "node": ">=8" } }, - "node_modules/wif": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", - "integrity": "sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "bs58check": "<3.0.0" - } - }, - "node_modules/wif/node_modules/base-x": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz", - "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==", - "license": "MIT", - "optional": true, - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/wif/node_modules/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", - "license": "MIT", - "optional": true, - "dependencies": { - "base-x": "^3.0.2" - } - }, - "node_modules/wif/node_modules/bs58check": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", - "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", - "license": "MIT", - "optional": true, - "dependencies": { - "bs58": "^4.0.0", - "create-hash": "^1.1.0", - "safe-buffer": "^5.1.2" - } - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -17099,15 +17662,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -17128,22 +17682,22 @@ "node": ">=8" } }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", "engines": { "node": ">=12" @@ -17152,6 +17706,21 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -17220,15 +17789,15 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", - "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" } }, "node_modules/yargs": { @@ -17266,15 +17835,6 @@ "node": ">=6" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/yargs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -17347,18 +17907,6 @@ "node": ">=8" } }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -17419,6 +17967,58 @@ "type": "github", "url": "https://github.com/sponsors/wooorm" } + }, + "packages/core": { + "name": "@opchan/core", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@noble/ed25519": "^2.2.3", + "@noble/hashes": "^1.8.0", + "@reown/appkit": "^1.7.17", + "@reown/appkit-adapter-bitcoin": "^1.7.17", + "@reown/appkit-adapter-wagmi": "^1.7.17", + "@reown/appkit-common": "^1.7.17", + "@reown/appkit-controllers": "^1.7.17", + "@waku/sdk": "^0.0.36-79dfb35.0", + "clsx": "^2.1.1", + "ordiscan": "^1.3.0", + "tailwind-merge": "^2.5.2", + "uuid": "^11.1.0", + "wagmi": "^2.17.0" + }, + "devDependencies": { + "@types/node": "^22.5.5", + "@types/uuid": "^10.0.0", + "typescript": "^5.5.3", + "vitest": "^3.2.4" + } + }, + "packages/core/node_modules/@types/node": { + "version": "22.18.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.6.tgz", + "integrity": "sha512-r8uszLPpeIWbNKtvWRt/DbVi5zbqZyj1PTmhRMqBMvDnaz1QpmSKujUtJLrqGZeoM8v72MfYggDceY4K1itzWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "packages/react": { + "name": "@opchan/react", + "version": "1.0.2", + "dependencies": { + "@opchan/core": "^1.0.0" + }, + "devDependencies": { + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "typescript": "^5.5.3" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } } } } diff --git a/package.json b/package.json index 12591b0..3a87249 100644 --- a/package.json +++ b/package.json @@ -1,110 +1,27 @@ { "name": "opchan", + "version": "1.0.0", + "description": "Browser-based Forum Library over Waku", "private": true, - "version": "0.1.0", - "description": "A decentralized forum built on Waku.", - "type": "module", + "workspaces": [ + "packages/*", + "app" + ], "scripts": { - "dev": "vite", - "build": "vite build", - "build:dev": "vite build --mode development", - "check": "tsc --project tsconfig.app.json --noEmit && tsc --project tsconfig.node.json --noEmit && eslint . --fix && prettier --write .", - "fix": "prettier --write . && eslint . --fix", - "preview": "vite preview", - "test": "vitest", - "test:ui": "vitest --ui" - }, - "dependencies": { - "@hookform/resolvers": "^3.9.0", - "@noble/ed25519": "^2.2.3", - "@noble/hashes": "^1.8.0", - "@radix-ui/react-accordion": "^1.2.0", - "@radix-ui/react-alert-dialog": "^1.1.1", - "@radix-ui/react-aspect-ratio": "^1.1.0", - "@radix-ui/react-avatar": "^1.1.0", - "@radix-ui/react-checkbox": "^1.1.1", - "@radix-ui/react-collapsible": "^1.1.0", - "@radix-ui/react-context-menu": "^2.2.1", - "@radix-ui/react-dialog": "^1.1.2", - "@radix-ui/react-dropdown-menu": "^2.1.1", - "@radix-ui/react-hover-card": "^1.1.1", - "@radix-ui/react-label": "^2.1.0", - "@radix-ui/react-menubar": "^1.1.1", - "@radix-ui/react-navigation-menu": "^1.2.0", - "@radix-ui/react-popover": "^1.1.1", - "@radix-ui/react-progress": "^1.1.0", - "@radix-ui/react-radio-group": "^1.2.0", - "@radix-ui/react-scroll-area": "^1.1.0", - "@radix-ui/react-select": "^2.1.1", - "@radix-ui/react-separator": "^1.1.0", - "@radix-ui/react-slider": "^1.2.0", - "@radix-ui/react-slot": "^1.1.0", - "@radix-ui/react-switch": "^1.1.0", - "@radix-ui/react-tabs": "^1.1.0", - "@radix-ui/react-toast": "^1.2.1", - "@radix-ui/react-toggle": "^1.1.0", - "@radix-ui/react-toggle-group": "^1.1.0", - "@radix-ui/react-tooltip": "^1.1.4", - "@reown/appkit": "^1.7.17", - "@reown/appkit-adapter-bitcoin": "^1.7.17", - "@reown/appkit-adapter-wagmi": "^1.7.17", - "@reown/appkit-wallet-button": "^1.7.17", - "@tanstack/react-query": "^5.84.1", - "@waku/sdk": "^0.0.35-67a7287.0", - "buffer": "^6.0.3", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "^1.0.0", - "date-fns": "^3.6.0", - "embla-carousel-react": "^8.3.0", - "input-otp": "^1.2.4", - "lucide-react": "^0.462.0", - "next-themes": "^0.3.0", - "ordiscan": "^1.3.0", - "re-resizable": "6.11.2", - "react": "^18.3.1", - "react-day-picker": "^8.10.1", - "react-dom": "^18.3.1", - "react-hook-form": "^7.53.0", - "react-markdown": "^10.1.0", - "react-resizable-panels": "^2.1.3", - "react-router-dom": "^6.26.2", - "recharts": "^2.12.7", - "rehype-sanitize": "^6.0.0", - "remark-gfm": "^4.0.1", - "sonner": "^1.5.0", - "tailwind-merge": "^2.5.2", - "tailwindcss-animate": "^1.0.7", - "uuid": "^11.1.0", - "vaul": "^0.9.3", - "viem": "^2.37.1", - "wagmi": "^2.16.1", - "zod": "^3.23.8" + "build": "npm run build --workspaces", + "test": "npm run test --workspaces", + "dev": "npm run dev --workspaces", + "lint": "npm run lint --workspaces" }, "devDependencies": { - "@eslint/js": "^9.9.0", - "@tailwindcss/typography": "^0.5.16", - "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.3.0", - "@testing-library/user-event": "^14.6.1", - "@types/node": "^22.5.5", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "@types/uuid": "^10.0.0", - "@vitejs/plugin-react-swc": "^3.5.0", - "@vitest/ui": "^3.2.4", - "autoprefixer": "^10.4.20", - "eslint": "^9.9.0", - "eslint-plugin-react-hooks": "^5.1.0-rc.0", - "eslint-plugin-react-refresh": "^0.4.9", - "globals": "^15.9.0", - "jsdom": "^26.1.0", - "postcss": "^8.4.47", - "prettier": "^3.6.2", - "tailwindcss": "^3.4.11", - "typescript": "^5.5.3", - "typescript-eslint": "^8.0.1", - "vite": "^5.4.1", - "vitest": "^3.2.4" + "typescript": "^5.0.0", + "@types/node": "^20.0.0", + "tsx": "^4.0.0", + "eslint": "^8.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0" + }, + "engines": { + "node": ">=18.0.0" } -} +} \ No newline at end of file diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000..78f54f4 --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,65 @@ +{ + "name": "@opchan/core", + "version": "1.0.0", + "description": "Core browser library for opchan", + "main": "dist/index.js", + "module": "dist/index.esm.js", + "types": "dist/index.d.ts", + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.esm.js", + "require": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "npm run clean && npm run build:cjs && cp dist/cjs/index.js dist/index.js && npm run build:esm && npm run build:types", + "build:cjs": "tsc --module commonjs --outDir dist/cjs", + "build:esm": "tsc --module esnext --outDir dist/esm && cp dist/esm/index.js dist/index.esm.js && cp -r dist/esm/types dist/ && cp -r dist/esm/lib dist/ && cp -r dist/esm/client dist/", + "build:types": "tsc --declaration --emitDeclarationOnly --outDir dist && cp dist/cjs/index.d.ts dist/index.d.ts", + "dev": "tsc --watch", + "test": "vitest", + "lint": "eslint src --ext .ts", + "clean": "rm -rf dist" + }, + "keywords": [ + "browser", + "library", + "typescript" + ], + "author": "", + "license": "MIT", + "dependencies": { + "@noble/ed25519": "^2.2.3", + "@noble/hashes": "^1.8.0", + "@reown/appkit": "^1.7.17", + "@reown/appkit-adapter-bitcoin": "^1.7.17", + "@reown/appkit-adapter-wagmi": "^1.7.17", + "@reown/appkit-common": "^1.7.17", + "@reown/appkit-controllers": "^1.7.17", + "@waku/sdk": "^0.0.36-79dfb35.0", + "clsx": "^2.1.1", + "ordiscan": "^1.3.0", + "tailwind-merge": "^2.5.2", + "uuid": "^11.1.0", + "wagmi": "^2.17.0" + }, + "devDependencies": { + "@types/node": "^22.5.5", + "@types/uuid": "^10.0.0", + "typescript": "^5.5.3", + "vitest": "^3.2.4" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not dead" + ] +} \ No newline at end of file diff --git a/packages/core/src/client/OpChanClient.ts b/packages/core/src/client/OpChanClient.ts new file mode 100644 index 0000000..795a298 --- /dev/null +++ b/packages/core/src/client/OpChanClient.ts @@ -0,0 +1,42 @@ +import { environment, type EnvironmentConfig } from '../lib/utils/environment'; +import messageManager, { DefaultMessageManager } from '../lib/waku'; +import { LocalDatabase, localDatabase } from '../lib/database/LocalDatabase'; +import { ForumActions } from '../lib/forum/ForumActions'; +import { RelevanceCalculator } from '../lib/forum/RelevanceCalculator'; +import { UserIdentityService } from '../lib/services/UserIdentityService'; +import { DelegationManager, delegationManager } from '../lib/delegation'; +import { walletManager } from '../lib/wallet'; +import { MessageService } from '../lib/services/MessageService'; + +export interface OpChanClientConfig { + ordiscanApiKey: string; +} + +export class OpChanClient { + readonly config: OpChanClientConfig; + + readonly messageManager: DefaultMessageManager = messageManager; + readonly database: LocalDatabase = localDatabase; + readonly forumActions = new ForumActions(); + readonly relevance = new RelevanceCalculator(); + readonly messageService: MessageService; + readonly userIdentityService: UserIdentityService; + readonly delegation: DelegationManager = delegationManager; + readonly wallet = walletManager; + + constructor(config: OpChanClientConfig) { + this.config = config; + + const env: EnvironmentConfig = { + + apiKeys: { + ordiscan: config.ordiscanApiKey, + }, + }; + + environment.configure(env); + + this.messageService = new MessageService(this.delegation); + this.userIdentityService = new UserIdentityService(this.messageService); + } +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 0000000..ff405fb --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1,51 @@ +/** + * @opchan/core - Browser library for opchan + */ + +// Export all types +export * from './types/forum'; +export * from './types/identity'; +export * from './types/waku'; + +// Export database functionality +export { LocalDatabase, localDatabase } from './lib/database/LocalDatabase'; +export * from './lib/database/schema'; + +// Export delegation system +export { + DelegationManager, + delegationManager, + DelegationStorage, + DelegationCrypto +} from './lib/delegation'; +export * from './lib/delegation/types'; +export type { DelegationFullStatus } from './lib/delegation'; + +// Export forum functionality +export { ForumActions } from './lib/forum/ForumActions'; +export { RelevanceCalculator } from './lib/forum/RelevanceCalculator'; +export * from './lib/forum/transformers'; + +// Export services +export { BookmarkService } from './lib/services/BookmarkService'; +export { MessageService } from './lib/services/MessageService'; +export { UserIdentityService, UserIdentity } from './lib/services/UserIdentityService'; +export { ordinals } from './lib/services/Ordinals'; + +// Export utilities +export * from './lib/utils'; +export { MessageValidator } from './lib/utils/MessageValidator'; +export { environment, type EnvironmentConfig } from './lib/utils/environment'; + +// Export Waku networking +export { default as messageManager } from './lib/waku'; +export * from './lib/waku/network'; + +// Export wallet functionality +export { WalletManager, walletManager } from './lib/wallet'; +export * from './lib/wallet/config'; +export * from './lib/wallet/types'; + +// Primary client API +export { OpChanClient, type OpChanClientConfig } from './client/OpChanClient'; + diff --git a/src/lib/database/LocalDatabase.ts b/packages/core/src/lib/database/LocalDatabase.ts similarity index 95% rename from src/lib/database/LocalDatabase.ts rename to packages/core/src/lib/database/LocalDatabase.ts index 237000c..0016070 100644 --- a/src/lib/database/LocalDatabase.ts +++ b/packages/core/src/lib/database/LocalDatabase.ts @@ -11,13 +11,13 @@ import { PostMessage, CommentMessage, VoteMessage, -} from '@/types/waku'; -import { OpchanMessage } from '@/types/forum'; -import { MessageValidator } from '@/lib/utils/MessageValidator'; -import { EVerificationStatus, User } from '@/types/identity'; -import { DelegationInfo } from '@/lib/delegation/types'; -import { openLocalDB, STORE, StoreName } from '@/lib/database/schema'; -import { Bookmark, BookmarkCache } from '@/types/forum'; +} from '../../types/waku'; +import { OpchanMessage } from '../../types/forum'; +import { MessageValidator } from '../utils/MessageValidator'; +import { EDisplayPreference, EVerificationStatus, User } from '../../types/identity'; +import { DelegationInfo } from '../delegation/types'; +import { openLocalDB, STORE, StoreName } from '../database/schema'; +import { Bookmark, BookmarkCache } from '../../types/forum'; export interface LocalDatabaseCache { cells: CellCache; @@ -235,8 +235,7 @@ export class LocalDatabase { displayPreference, lastUpdated: timestamp, verificationStatus: - existing?.verificationStatus ?? - EVerificationStatus.WALLET_UNCONNECTED, + existing?.verificationStatus } as UserIdentityCache[string]; this.cache.userIdentities[author] = nextRecord; @@ -648,21 +647,17 @@ export class LocalDatabase { address: string, record: Partial & { lastUpdated?: number } ): Promise { + // Retrieve existing identity or initialize with proper defaults const existing: UserIdentityCache[string] = this.cache.userIdentities[address] || - ({ + { ensName: undefined, ordinalDetails: undefined, callSign: undefined, - displayPreference: EVerificationStatus.WALLET_UNCONNECTED - ? (undefined as never) - : (undefined as never), - // We'll set displayPreference when we receive a profile update; leave as - // WALLET_ADDRESS by default for correctness. - // Casting below ensures the object satisfies the interface at compile time. + displayPreference: EDisplayPreference.WALLET_ADDRESS, lastUpdated: 0, verificationStatus: EVerificationStatus.WALLET_UNCONNECTED, - } as unknown as UserIdentityCache[string]); + }; const merged: UserIdentityCache[string] = { ...existing, @@ -671,7 +666,7 @@ export class LocalDatabase { existing.lastUpdated ?? 0, record.lastUpdated ?? Date.now() ), - } as UserIdentityCache[string]; + }; this.cache.userIdentities[address] = merged; this.put(STORE.USER_IDENTITIES, { address, ...merged }); diff --git a/src/lib/database/schema.ts b/packages/core/src/lib/database/schema.ts similarity index 99% rename from src/lib/database/schema.ts rename to packages/core/src/lib/database/schema.ts index e133610..77ee96c 100644 --- a/src/lib/database/schema.ts +++ b/packages/core/src/lib/database/schema.ts @@ -1,5 +1,5 @@ export const DB_NAME = 'opchan-local'; -export const DB_VERSION = 3; +export const DB_VERSION = 4; export const STORE = { CELLS: 'cells', diff --git a/src/lib/delegation/crypto.ts b/packages/core/src/lib/delegation/crypto.ts similarity index 97% rename from src/lib/delegation/crypto.ts rename to packages/core/src/lib/delegation/crypto.ts index d5daa15..730e560 100644 --- a/src/lib/delegation/crypto.ts +++ b/packages/core/src/lib/delegation/crypto.ts @@ -1,7 +1,7 @@ import * as ed from '@noble/ed25519'; import { sha512 } from '@noble/hashes/sha512'; -import { bytesToHex, hexToBytes } from '@/lib/utils'; -import { WalletManager } from '@/lib/wallet'; +import { bytesToHex, hexToBytes } from '../utils'; +import { WalletManager } from '../wallet'; // Set up ed25519 with sha512 ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m)); diff --git a/src/lib/delegation/index.ts b/packages/core/src/lib/delegation/index.ts similarity index 96% rename from src/lib/delegation/index.ts rename to packages/core/src/lib/delegation/index.ts index da36b11..3c5589f 100644 --- a/src/lib/delegation/index.ts +++ b/packages/core/src/lib/delegation/index.ts @@ -1,5 +1,5 @@ -import { OpchanMessage } from '@/types/forum'; -import { UnsignedMessage } from '@/types/waku'; +import { OpchanMessage } from '../../types/forum'; +import { UnsignedMessage } from '../../types/waku'; import { DelegationDuration, DelegationInfo, @@ -286,8 +286,8 @@ export class DelegationManager { !proof?.walletAddress || !proof?.authMessage || proof?.expiryTimestamp === undefined || - proof.walletAddress !== expectedWalletAddress || - Date.now() >= proof.expiryTimestamp + proof.walletAddress !== expectedWalletAddress + // Date.now() >= proof.expiryTimestamp ) { return false; } @@ -329,9 +329,9 @@ export class DelegationManager { if (proof.walletAddress !== expectedWalletAddress) { reasons.push('Delegation wallet address does not match author'); } - if (Date.now() >= proof.expiryTimestamp) { - reasons.push('Delegation has expired'); - } + // if (Date.now() >= proof.expiryTimestamp) { + // reasons.push('Delegation has expired'); + // } if ( !proof.authMessage.includes(expectedWalletAddress) || !proof.authMessage.includes(expectedBrowserKey) || @@ -361,4 +361,4 @@ export class DelegationManager { // Export singleton instance export const delegationManager = new DelegationManager(); export * from './types'; -export { DelegationStorage }; +export { DelegationStorage, DelegationCrypto }; diff --git a/src/lib/delegation/storage.ts b/packages/core/src/lib/delegation/storage.ts similarity index 73% rename from src/lib/delegation/storage.ts rename to packages/core/src/lib/delegation/storage.ts index 78c6a7a..a340bbf 100644 --- a/src/lib/delegation/storage.ts +++ b/packages/core/src/lib/delegation/storage.ts @@ -1,16 +1,12 @@ -import { localDatabase } from '@/lib/database/LocalDatabase'; +import { localDatabase } from '../database/LocalDatabase'; import { DelegationInfo } from './types'; +import { environment } from '../utils/environment'; export class DelegationStorage { /** * Store delegation information in IndexedDB */ static async store(delegation: DelegationInfo): Promise { - // Reduce verbose logging in production; keep minimal signal - if (import.meta.env?.MODE !== 'production') { - console.log('DelegationStorage.store'); - } - try { await localDatabase.storeDelegation(delegation); } catch (e) { @@ -24,9 +20,6 @@ export class DelegationStorage { static async retrieve(): Promise { try { const delegation = await localDatabase.loadDelegation(); - if (import.meta.env?.MODE !== 'production') { - console.log('DelegationStorage.retrieve'); - } return delegation; } catch (e) { console.error('Failed to retrieve delegation information', e); diff --git a/src/lib/delegation/types.ts b/packages/core/src/lib/delegation/types.ts similarity index 100% rename from src/lib/delegation/types.ts rename to packages/core/src/lib/delegation/types.ts diff --git a/src/lib/forum/ForumActions.ts b/packages/core/src/lib/forum/ForumActions.ts similarity index 96% rename from src/lib/forum/ForumActions.ts rename to packages/core/src/lib/forum/ForumActions.ts index 3272d4f..254fabf 100644 --- a/src/lib/forum/ForumActions.ts +++ b/packages/core/src/lib/forum/ForumActions.ts @@ -10,13 +10,13 @@ import { CommentMessage, PostMessage, EModerationAction, -} from '@/types/waku'; -import { Cell, Comment, Post } from '@/types/forum'; -import { EVerificationStatus, User } from '@/types/identity'; +} from '../../types/waku'; +import { Cell, Comment, Post } from '../../types/forum'; +import { EVerificationStatus, User } from '../../types/identity'; import { transformCell, transformComment, transformPost } from './transformers'; -import { DelegationManager } from '@/lib/delegation'; -import { localDatabase } from '@/lib/database/LocalDatabase'; -import messageManager from '@/lib/waku'; +import { DelegationManager } from '../delegation'; +import { localDatabase } from '../database/LocalDatabase'; +import messageManager from '../waku'; type ActionResult = { success: boolean; @@ -363,7 +363,7 @@ export class ForumActions { return { success: false, error: - 'Authentication required. You need to verify Ordinal ownership to moderate posts.', + 'Authentication required. Connect your wallet to moderate posts.', }; } if (currentUser.address !== cellOwner) { @@ -437,7 +437,7 @@ export class ForumActions { return { success: false, error: - 'Authentication required. You need to verify Ordinal ownership to moderate comments.', + 'Authentication required. Connect your wallet to moderate comments.', }; } if (currentUser.address !== cellOwner) { @@ -511,7 +511,7 @@ export class ForumActions { return { success: false, error: - 'Authentication required. You need to verify Ordinal ownership to moderate users.', + 'Authentication required. Connect your wallet to moderate users.', }; } if (currentUser.address !== cellOwner) { @@ -579,7 +579,7 @@ export class ForumActions { return { success: false, error: - 'Authentication required. You need to verify Ordinal ownership to unmoderate posts.', + 'Authentication required. Connect your wallet to unmoderate posts.', }; } if (currentUser.address !== cellOwner) { @@ -653,7 +653,7 @@ export class ForumActions { return { success: false, error: - 'Authentication required. You need to verify Ordinal ownership to unmoderate comments.', + 'Authentication required. Connect your wallet to unmoderate comments.', }; } if (currentUser.address !== cellOwner) { @@ -727,7 +727,7 @@ export class ForumActions { return { success: false, error: - 'Authentication required. You need to verify Ordinal ownership to unmoderate users.', + 'Authentication required. Connect your wallet to unmoderate users.', }; } if (currentUser.address !== cellOwner) { diff --git a/src/lib/forum/RelevanceCalculator.ts b/packages/core/src/lib/forum/RelevanceCalculator.ts similarity index 98% rename from src/lib/forum/RelevanceCalculator.ts rename to packages/core/src/lib/forum/RelevanceCalculator.ts index b14eab7..8bfdea2 100644 --- a/src/lib/forum/RelevanceCalculator.ts +++ b/packages/core/src/lib/forum/RelevanceCalculator.ts @@ -4,9 +4,9 @@ import { Cell, RelevanceScoreDetails, UserVerificationStatus, -} from '@/types/forum'; -import { EVerificationStatus, User } from '@/types/identity'; -import { VoteMessage } from '@/types/waku'; +} from '../../types/forum'; +import { EVerificationStatus, User } from '../../types/identity'; +import { VoteMessage } from '../../types/waku'; export class RelevanceCalculator { private static readonly BASE_SCORES = { diff --git a/src/lib/forum/__tests__/relevance.test.ts b/packages/core/src/lib/forum/__tests__/relevance.test.ts similarity index 97% rename from src/lib/forum/__tests__/relevance.test.ts rename to packages/core/src/lib/forum/__tests__/relevance.test.ts index e7c8643..aac6e11 100644 --- a/src/lib/forum/__tests__/relevance.test.ts +++ b/packages/core/src/lib/forum/__tests__/relevance.test.ts @@ -1,12 +1,12 @@ import { RelevanceCalculator } from '../RelevanceCalculator'; -import { Post, Comment, UserVerificationStatus } from '@/types/forum'; +import { Post, Comment, UserVerificationStatus } from '../../types/forum'; import { User, EVerificationStatus, EDisplayPreference, -} from '@/types/identity'; -import { VoteMessage, MessageType } from '@/types/waku'; -import { DelegationProof } from '@/lib/delegation/types'; +} from '../../types/identity'; +import { VoteMessage, MessageType } from '../../types/waku'; +import { DelegationProof } from '../delegation/types'; import { expect, describe, beforeEach, it } from 'vitest'; // Mock delegation proof for tests diff --git a/src/lib/forum/transformers.ts b/packages/core/src/lib/forum/transformers.ts similarity index 78% rename from src/lib/forum/transformers.ts rename to packages/core/src/lib/forum/transformers.ts index 4fb64f3..d43c8f1 100644 --- a/src/lib/forum/transformers.ts +++ b/packages/core/src/lib/forum/transformers.ts @@ -1,16 +1,16 @@ -import { Cell, Post, Comment } from '@/types/forum'; +import { Cell, Post, Comment } from '../../types/forum'; import { CellMessage, CommentMessage, PostMessage, VoteMessage, + ModerateMessage, EModerationAction, -} from '@/types/waku'; -import messageManager from '@/lib/waku'; +} from '../../types/waku'; +import { localDatabase } from '../database/LocalDatabase'; import { RelevanceCalculator } from './RelevanceCalculator'; -import { UserVerificationStatus } from '@/types/forum'; -// Validation is enforced at ingestion time by LocalDatabase. Transformers assume -// cache contains only valid, verified messages. +import { UserVerificationStatus } from '../../types/forum'; +import { EVerificationStatus } from '../../types/identity'; export const transformCell = async ( cellMessage: CellMessage, @@ -67,8 +67,8 @@ export const transformPost = async ( ): Promise => { // Message validity already enforced upstream - const votes = Object.values(messageManager.messageCache.votes).filter( - vote => vote.targetId === postMessage.id + const votes = Object.values(localDatabase.cache.votes).filter( + (vote): vote is VoteMessage => vote && vote.targetId === postMessage.id ); // Votes in cache are already validated; just map const filteredVotes = votes; @@ -79,15 +79,16 @@ export const transformPost = async ( (vote): vote is VoteMessage => vote !== null && vote.value === -1 ); - const modMsg = messageManager.messageCache.moderations[postMessage.id]; + const modMsg = localDatabase.cache.moderations[postMessage.id]; const isPostModerated = !!modMsg && modMsg.targetType === 'post' && modMsg.action === EModerationAction.MODERATE; const userModMsg = Object.values( - messageManager.messageCache.moderations + localDatabase.cache.moderations ).find( - m => + (m): m is ModerateMessage => + m && m.targetType === 'user' && m.cellId === postMessage.cellId && m.targetId === postMessage.author @@ -135,9 +136,11 @@ export const transformPost = async ( // Get comments for this post const comments = await Promise.all( - Object.values(messageManager.messageCache.comments).map(comment => - transformComment(comment, undefined, userVerificationStatus) - ) + Object.values(localDatabase.cache.comments) + .filter((comment): comment is CommentMessage => comment !== null) + .map(comment => + transformComment(comment, undefined, userVerificationStatus) + ) ).then(comments => comments.filter((comment): comment is Comment => comment !== null) ); @@ -186,8 +189,8 @@ export const transformComment = async ( userVerificationStatus?: UserVerificationStatus ): Promise => { // Message validity already enforced upstream - const votes = Object.values(messageManager.messageCache.votes).filter( - vote => vote.targetId === commentMessage.id + const votes = Object.values(localDatabase.cache.votes).filter( + (vote): vote is VoteMessage => vote && vote.targetId === commentMessage.id ); // Votes in cache are already validated const filteredVotes = votes; @@ -198,19 +201,20 @@ export const transformComment = async ( (vote): vote is VoteMessage => vote !== null && vote.value === -1 ); - const modMsg = messageManager.messageCache.moderations[commentMessage.id]; + const modMsg = localDatabase.cache.moderations[commentMessage.id]; const isCommentModerated = !!modMsg && modMsg.targetType === 'comment' && modMsg.action === EModerationAction.MODERATE; // Find the post to get the correct cell ID - const parentPost = Object.values(messageManager.messageCache.posts).find( - post => post.id === commentMessage.postId + const parentPost = Object.values(localDatabase.cache.posts).find( + (post): post is PostMessage => post && post.id === commentMessage.postId ); const userModMsg = Object.values( - messageManager.messageCache.moderations + localDatabase.cache.moderations ).find( - m => + (m): m is ModerateMessage => + m && m.targetType === 'user' && m.cellId === parentPost?.cellId && m.targetId === commentMessage.author @@ -281,29 +285,45 @@ export const transformVote = async ( export const getDataFromCache = async ( _verifyMessage?: unknown, // Deprecated parameter, kept for compatibility - userVerificationStatus?: UserVerificationStatus ): Promise<{ cells: Cell[]; posts: Post[]; comments: Comment[] }> => { - // First transform posts and comments to get relevance scores - // All validation is now handled internally by the transform functions + const userIdentities = localDatabase.cache.userIdentities; + const userVerificationStatus: UserVerificationStatus = {}; + + for (const [address, rec] of Object.entries(userIdentities)) { + userVerificationStatus[address] = { + isVerified: rec.verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED, + hasENS: Boolean(rec.ensName), + hasOrdinal: Boolean(rec.ordinalDetails), + ensName: rec.ensName, + verificationStatus: rec.verificationStatus, + }; + } + const posts = await Promise.all( - Object.values(messageManager.messageCache.posts).map(post => - transformPost(post, undefined, userVerificationStatus) - ) + Object.values(localDatabase.cache.posts) + .filter((post): post is PostMessage => post !== null) + .map(post => + transformPost(post, undefined, userVerificationStatus) + ) ).then(posts => posts.filter((post): post is Post => post !== null)); const comments = await Promise.all( - Object.values(messageManager.messageCache.comments).map(c => - transformComment(c, undefined, userVerificationStatus) - ) + Object.values(localDatabase.cache.comments) + .filter((c): c is CommentMessage => c !== null) + .map(c => + transformComment(c, undefined, userVerificationStatus) + ) ).then(comments => comments.filter((comment): comment is Comment => comment !== null) ); // Then transform cells with posts for relevance calculation const cells = await Promise.all( - Object.values(messageManager.messageCache.cells).map(cell => - transformCell(cell, undefined, userVerificationStatus, posts) - ) + Object.values(localDatabase.cache.cells) + .filter((cell): cell is CellMessage => cell !== null) + .map(cell => + transformCell(cell, undefined, userVerificationStatus, posts) + ) ).then(cells => cells.filter((cell): cell is Cell => cell !== null)); return { cells, posts, comments }; diff --git a/src/lib/services/BookmarkService.ts b/packages/core/src/lib/services/BookmarkService.ts similarity index 96% rename from src/lib/services/BookmarkService.ts rename to packages/core/src/lib/services/BookmarkService.ts index 486efaf..6fa364d 100644 --- a/src/lib/services/BookmarkService.ts +++ b/packages/core/src/lib/services/BookmarkService.ts @@ -1,5 +1,5 @@ -import { Bookmark, BookmarkType, Post, Comment } from '@/types/forum'; -import { localDatabase } from '@/lib/database/LocalDatabase'; +import { Bookmark, BookmarkType, Post, Comment } from '../../types/forum'; +import { localDatabase } from '../database/LocalDatabase'; /** * Service for managing bookmarks diff --git a/src/lib/services/MessageService.ts b/packages/core/src/lib/services/MessageService.ts similarity index 93% rename from src/lib/services/MessageService.ts rename to packages/core/src/lib/services/MessageService.ts index f7f5567..d651b60 100644 --- a/src/lib/services/MessageService.ts +++ b/packages/core/src/lib/services/MessageService.ts @@ -1,7 +1,7 @@ -import { OpchanMessage } from '@/types/forum'; -import { UnsignedMessage } from '@/types/waku'; -import { DelegationManager } from '@/lib/delegation'; -import messageManager from '@/lib/waku'; +import { OpchanMessage } from '../../types/forum'; +import { UnsignedMessage } from '../../types/waku'; +import { DelegationManager } from '../delegation'; +import messageManager from '../waku'; export interface MessageResult { success: boolean; @@ -24,6 +24,8 @@ export class MessageService implements MessageServiceInterface { this.delegationManager = delegationManager; } + // ===== PUBLIC METHODS ===== + /** * Sign and send a message to the Waku network */ diff --git a/src/lib/services/Ordinals.ts b/packages/core/src/lib/services/Ordinals.ts similarity index 66% rename from src/lib/services/Ordinals.ts rename to packages/core/src/lib/services/Ordinals.ts index 9513d71..f540410 100644 --- a/src/lib/services/Ordinals.ts +++ b/packages/core/src/lib/services/Ordinals.ts @@ -1,5 +1,5 @@ import { Ordiscan, Inscription } from 'ordiscan'; -const API_KEY = import.meta.env.VITE_ORDISCAN_API; +import { environment } from '../utils/environment'; class Ordinals { private static instance: Ordinals | null = null; @@ -11,13 +11,21 @@ class Ordinals { this.ordiscan = ordiscan; } + // ===== PUBLIC STATIC METHODS ===== + static getInstance(): Ordinals { if (!Ordinals.instance) { - Ordinals.instance = new Ordinals(new Ordiscan(API_KEY)); + const apiKey = environment.ordiscanApiKey; + if (!apiKey) { + throw new Error('Ordiscan API key is not configured. Please set up the environment.'); + } + Ordinals.instance = new Ordinals(new Ordiscan(apiKey)); } return Ordinals.instance; } + // ===== PUBLIC INSTANCE METHODS ===== + /** * Get Ordinal details for a Bitcoin address */ @@ -44,4 +52,9 @@ class Ordinals { } } -export const ordinals = Ordinals.getInstance(); +export const ordinals = { + getInstance: () => Ordinals.getInstance(), + getOrdinalDetails: async (address: string) => { + return Ordinals.getInstance().getOrdinalDetails(address); + } +}; diff --git a/packages/core/src/lib/services/UserIdentityService.ts b/packages/core/src/lib/services/UserIdentityService.ts new file mode 100644 index 0000000..eab0451 --- /dev/null +++ b/packages/core/src/lib/services/UserIdentityService.ts @@ -0,0 +1,404 @@ +import { EVerificationStatus, EDisplayPreference } from '../../types/identity'; +import { + UnsignedUserProfileUpdateMessage, + UserProfileUpdateMessage, + MessageType, + UserIdentityCache, +} from '../../types/waku'; +import { MessageService } from './MessageService'; +import { localDatabase } from '../database/LocalDatabase'; +import { walletManager, WalletManager } from '../wallet'; + +export interface UserIdentity { + address: string; + ensName?: string; + ordinalDetails?: { + ordinalId: string; + ordinalDetails: string; + }; + callSign?: string; + displayPreference: EDisplayPreference; + displayName: string; + lastUpdated: number; + verificationStatus: EVerificationStatus; +} + +export class UserIdentityService { + private messageService: MessageService; + private refreshListeners: Set<(address: string) => void> = new Set(); + private debounceTimers: Map = new Map(); + + constructor(messageService: MessageService) { + this.messageService = messageService; + } + + // ===== PUBLIC METHODS ===== + + /** + * Unified identity getter. When opts.fresh === true, bypass caches. + */ + async getIdentity( + address: string, + opts?: { fresh?: boolean } + ): Promise { + console.log('getIdentity', address, opts); + if (opts?.fresh) { + return this.getUserIdentityFresh(address); + } + + // Debounce rapid calls for non-fresh path + if (this.debounceTimers.has(address)) { + clearTimeout(this.debounceTimers.get(address)!); + } + + return new Promise((resolve) => { + const timer = setTimeout(async () => { + this.debounceTimers.delete(address); + const result = await this.getUserIdentityInternal(address); + resolve(result); + }, 100); + this.debounceTimers.set(address, timer); + }); + } + + /** + * Force a fresh identity resolution bypassing caches and LocalDatabase. + * Useful for explicit verification flows where we must hit upstream resolvers. + */ + async getUserIdentityFresh(address: string): Promise { + const identity = await this.resolveUserIdentity(address); + + if (identity) { + // Persist the fresh identity to LocalDatabase + await localDatabase.upsertUserIdentity(address, identity); + } + return identity; + } + + /** + * Get all cached user identities + */ + getAll(): UserIdentity[] { + return Object.entries(localDatabase.cache.userIdentities).map(([address, cached]) => ({ + address, + ...cached, + verificationStatus: this.mapVerificationStatus(cached.verificationStatus), + })); + } + + /** + * New contract: return result and updated identity. + */ + async updateProfile( + address: string, + updates: { callSign?: string; displayPreference?: EDisplayPreference } + ): Promise<{ ok: true; identity: UserIdentity } | { ok: false; error: Error }>{ + try { + const callSign = updates.callSign?.trim() || undefined; + const displayPreference = + updates.displayPreference ?? + localDatabase.cache.userIdentities[address]?.displayPreference ?? + EDisplayPreference.WALLET_ADDRESS; + + const timestamp = Date.now(); + const unsignedMessage: UnsignedUserProfileUpdateMessage = { + id: crypto.randomUUID(), + type: MessageType.USER_PROFILE_UPDATE, + timestamp, + author: address, + displayPreference, + }; + if (callSign) unsignedMessage.callSign = callSign; + + const signedMessage = await this.messageService.signAndBroadcastMessage(unsignedMessage); + if (!signedMessage) return { ok: false, error: new Error('Broadcast failed') }; + + const profileMessage: UserProfileUpdateMessage = { + id: unsignedMessage.id, + type: MessageType.USER_PROFILE_UPDATE, + timestamp, + author: address, + displayPreference, + signature: signedMessage.signature, + browserPubKey: signedMessage.browserPubKey, + delegationProof: signedMessage.delegationProof, + ...(callSign ? { callSign } : {}), + }; + + // Persist, notify + await localDatabase.applyMessage(profileMessage); + this.notifyRefreshListeners(address); + + const identity = await this.getIdentity(address); + if (!identity) return { ok: false, error: new Error('Identity unavailable') }; + return { ok: true, identity }; + } catch (error) { + return { ok: false, error: error as Error }; + } + } + + /** + * Update user identity from Waku message + */ + updateUserIdentityFromMessage(message: UserProfileUpdateMessage): void { + // No-op: LocalDatabase.applyMessage mutates the canonical cache. + // We only need to notify listeners to refresh their local views. + this.notifyRefreshListeners(message.author); + } + + /** + * Refresh user identity (force re-resolution) + */ + async refreshIdentity(address: string): Promise { + await this.getIdentity(address, { fresh: true }); + } + + /** + * Clear user identity cache + */ + clearCache(): void { + this.debounceTimers.forEach(timer => clearTimeout(timer)); + this.debounceTimers.clear(); + } + + /** + * Subscribe with identity payload + */ + subscribe( + listener: (address: string, identity: UserIdentity | null) => void + ): () => void { + const wrapped = async (address: string) => { + const record = localDatabase.cache.userIdentities[address]; + const identity = record + ? this.buildUserIdentityFromRecord(address, record) + : await this.getIdentity(address); + listener(address, identity); + }; + this.refreshListeners.add(wrapped); + return () => this.refreshListeners.delete(wrapped); + } + + /** + * Get display name for user based on their preferences + */ + getDisplayName({address, ensName, displayPreference}: {address: string, ensName?: string | null, displayPreference?: EDisplayPreference}): string { + const storedIdentity = localDatabase.cache.userIdentities[address]; + if ( + storedIdentity?.callSign && + displayPreference === EDisplayPreference.CALL_SIGN + ) { + return storedIdentity.callSign; + } + + if (ensName) { + return ensName; + } + + return `${address.slice(0, 6)}...${address.slice(-4)}`; + } + + // ===== PRIVATE METHODS ===== + + /** + * Internal method to get user identity without debouncing + */ + private async getUserIdentityInternal(address: string): Promise { + const record = localDatabase.cache.userIdentities[address]; + if (record) { + let identity = this.buildUserIdentityFromRecord(address, record); + identity = await this.ensureEnsEnriched(address, identity); + return identity; + } + + // Try to resolve identity from various sources + const resolved = await this.resolveUserIdentity(address); + if (resolved) { + // Persist the resolved identity to LocalDatabase for future use + await localDatabase.upsertUserIdentity(address, resolved); + + return resolved; + } + + return null; + } + + /** + * Resolve user identity from various sources + */ + private async resolveUserIdentity( + address: string + ): Promise { + try { + console.log('resolveUserIdentity', address); + const [ensName, ordinalDetails] = await Promise.all([ + this.resolveENSName(address), + this.resolveOrdinalDetails(address), + ]); + + const isWalletConnected = WalletManager.hasInstance() + ? walletManager.getInstance().isConnected() + : false; + let verificationStatus: EVerificationStatus; + if (ensName || ordinalDetails) { + verificationStatus = EVerificationStatus.ENS_ORDINAL_VERIFIED; + } else { + verificationStatus = isWalletConnected ? EVerificationStatus.WALLET_CONNECTED : EVerificationStatus.WALLET_UNCONNECTED; + } + + const displayPreference = localDatabase.cache.userIdentities[address]?.displayPreference ?? EDisplayPreference.WALLET_ADDRESS; + + + return { + address, + ensName: ensName || undefined, + ordinalDetails: ordinalDetails || undefined, + callSign: undefined, // Will be populated from Waku messages + displayPreference: displayPreference, + displayName: this.getDisplayName({address, ensName, displayPreference}), + lastUpdated: Date.now(), + verificationStatus, + }; + } catch (error) { + console.error('Failed to resolve user identity:', error); + return null; + } + } + + /** + * Resolve ENS name from Ethereum address with caching to prevent multiple calls + */ + private async resolveENSName(address: string): Promise { + if (!address.startsWith('0x')) { + return null; // Not an Ethereum address + } + + // Prefer previously persisted ENS if recent + const cached = localDatabase.cache.userIdentities[address]; + if (cached?.ensName && cached.lastUpdated > Date.now() - 300000) { + return cached.ensName; + } + + return this.doResolveENSName(address); + } + + /** + * Resolve Ordinal details from Bitcoin address + */ + private async resolveOrdinalDetails( + address: string + ): Promise<{ ordinalId: string; ordinalDetails: string } | null> { + try { + if (address.startsWith('0x')) { + return null; + } + + const inscriptions = await WalletManager.resolveOperatorOrdinals(address); + if (Array.isArray(inscriptions) && inscriptions.length > 0) { + const first = inscriptions[0]!; + return { + ordinalId: first.inscription_id, + ordinalDetails: + first.parent_inscription_id || 'Operator badge present', + }; + } + return null; + } catch (error) { + console.error('Failed to resolve Ordinal details:', error); + return null; + } + } + + /** + * Notify all listeners that user identity data has changed + */ + private notifyRefreshListeners(address: string): void { + this.refreshListeners.forEach(listener => listener(address)); + } + + // ===== HELPER METHODS ===== + + /** + * Normalize a cached identity record into a strongly-typed UserIdentity + */ + private buildUserIdentityFromRecord( + address: string, + record: UserIdentityCache[string] + ): UserIdentity { + return { + address, + ensName: record.ensName, + ordinalDetails: record.ordinalDetails, + callSign: record.callSign, + displayPreference: record.displayPreference, + displayName: this.getDisplayName({address, ensName: record.ensName, displayPreference: record.displayPreference}), + lastUpdated: record.lastUpdated, + verificationStatus: this.mapVerificationStatus(record.verificationStatus), + }; + } + + /** + * Ensure ENS is enriched if missing. Persists updates and keeps caches in sync. + */ + private async ensureEnsEnriched( + address: string, + identity: UserIdentity + ): Promise { + if (!identity.ensName && address.startsWith('0x')) { + const ensName = await this.resolveENSName(address); + if (ensName) { + const updated: UserIdentity = { + ...identity, + ensName, + verificationStatus: EVerificationStatus.ENS_ORDINAL_VERIFIED, + lastUpdated: Date.now(), + }; + + await localDatabase.upsertUserIdentity(address, { + ensName, + verificationStatus: EVerificationStatus.ENS_ORDINAL_VERIFIED, + lastUpdated: updated.lastUpdated, + }); + + return updated; + } + } + return identity; + } + + private async doResolveENSName(address: string): Promise { + try { + // Resolve ENS via centralized WalletManager helper + const ensName = await WalletManager.resolveENS(address); + return ensName || null; + } catch (error) { + console.error('Failed to resolve ENS name:', error); + return null; + } + } + + /** + * Map verification status string to enum + */ + private mapVerificationStatus(status: string): EVerificationStatus { + switch (status) { + // Legacy message-cache statuses + case 'verified-basic': + return EVerificationStatus.WALLET_CONNECTED; + case 'verified-owner': + return EVerificationStatus.ENS_ORDINAL_VERIFIED; + case 'verifying': + return EVerificationStatus.WALLET_CONNECTED; // Temporary state during verification + + // Enum string values persisted in LocalDatabase + case EVerificationStatus.WALLET_UNCONNECTED: + return EVerificationStatus.WALLET_UNCONNECTED; + case EVerificationStatus.WALLET_CONNECTED: + return EVerificationStatus.WALLET_CONNECTED; + case EVerificationStatus.ENS_ORDINAL_VERIFIED: + return EVerificationStatus.ENS_ORDINAL_VERIFIED; + + default: + return EVerificationStatus.WALLET_UNCONNECTED; + } + } +} diff --git a/src/lib/services/index.ts b/packages/core/src/lib/services/index.ts similarity index 100% rename from src/lib/services/index.ts rename to packages/core/src/lib/services/index.ts diff --git a/src/lib/utils.ts b/packages/core/src/lib/utils.ts similarity index 72% rename from src/lib/utils.ts rename to packages/core/src/lib/utils.ts index af207fc..2c0ca44 100644 --- a/src/lib/utils.ts +++ b/packages/core/src/lib/utils.ts @@ -1,10 +1,3 @@ -import { clsx, type ClassValue } from 'clsx'; -import { twMerge } from 'tailwind-merge'; - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); -} - export function bytesToHex(bytes: Uint8Array): string { return Array.from(bytes) .map(b => b.toString(16).padStart(2, '0')) diff --git a/src/lib/utils/MessageValidator.ts b/packages/core/src/lib/utils/MessageValidator.ts similarity index 98% rename from src/lib/utils/MessageValidator.ts rename to packages/core/src/lib/utils/MessageValidator.ts index 351ffa8..72cc342 100644 --- a/src/lib/utils/MessageValidator.ts +++ b/packages/core/src/lib/utils/MessageValidator.ts @@ -1,5 +1,5 @@ -import { OpchanMessage, PartialMessage } from '@/types/forum'; -import { DelegationManager } from '@/lib/delegation'; +import { OpchanMessage, PartialMessage } from '../../types/forum'; +import { DelegationManager } from '../delegation'; interface ValidationReport { validMessages: OpchanMessage[]; diff --git a/packages/core/src/lib/utils/environment.ts b/packages/core/src/lib/utils/environment.ts new file mode 100644 index 0000000..9a16e16 --- /dev/null +++ b/packages/core/src/lib/utils/environment.ts @@ -0,0 +1,25 @@ +/** + * Environment abstraction for the core library + * This allows the library to work in different environments (Vite, Node, etc.) + */ + +export interface EnvironmentConfig { + apiKeys?: { + ordiscan?: string; + }; +} + +class Environment { + private config: EnvironmentConfig = { + }; + + public configure(config: EnvironmentConfig): void { + this.config = { ...this.config, ...config }; + } + + public get ordiscanApiKey(): string | undefined { + return this.config.apiKeys?.ordiscan; + } +} + +export const environment = new Environment(); diff --git a/src/lib/waku/CodecManager.ts b/packages/core/src/lib/waku/CodecManager.ts similarity index 94% rename from src/lib/waku/CodecManager.ts rename to packages/core/src/lib/waku/CodecManager.ts index 07cab69..c3c400c 100644 --- a/src/lib/waku/CodecManager.ts +++ b/packages/core/src/lib/waku/CodecManager.ts @@ -8,7 +8,7 @@ import { ModerateMessage, } from '../../types/waku'; import { CONTENT_TOPIC } from './constants'; -import { OpchanMessage } from '@/types/forum'; +import { OpchanMessage } from '../../types/forum'; export class CodecManager { private encoder: IEncoder; @@ -48,7 +48,7 @@ export class CodecManager { case MessageType.USER_PROFILE_UPDATE: return message as UserProfileUpdateMessage; default: - throw new Error(`Unknown message type: `, message); + throw new Error(`Unknown message type: ${JSON.stringify(message)}`); } } diff --git a/packages/core/src/lib/waku/constants.ts b/packages/core/src/lib/waku/constants.ts new file mode 100644 index 0000000..d6a7160 --- /dev/null +++ b/packages/core/src/lib/waku/constants.ts @@ -0,0 +1,6 @@ +/** + * Single content topic for all message types + * Different message types are parsed from the message content itself + */ +export const CONTENT_TOPIC = '/opchan-demo/1/messages/proto'; + diff --git a/src/lib/waku/core/ReliableMessaging.ts b/packages/core/src/lib/waku/core/ReliableMessaging.ts similarity index 88% rename from src/lib/waku/core/ReliableMessaging.ts rename to packages/core/src/lib/waku/core/ReliableMessaging.ts index 1f6dc96..5d84127 100644 --- a/src/lib/waku/core/ReliableMessaging.ts +++ b/packages/core/src/lib/waku/core/ReliableMessaging.ts @@ -2,11 +2,11 @@ import { IDecodedMessage, LightNode, ReliableChannel, - ReliableChannelEvent, + ReliableChannelEvents, } from '@waku/sdk'; import { CodecManager } from '../CodecManager'; -import { generateStringId } from '@/lib/utils'; -import { OpchanMessage } from '@/types/forum'; +import { generateStringId } from '../../utils'; +import { OpchanMessage } from '../../../types/forum'; export interface MessageStatusCallback { onSent?: (messageId: string) => void; @@ -27,78 +27,12 @@ export class ReliableMessaging { this.initializeChannel(node); } - private async initializeChannel(node: LightNode): Promise { - const encoder = this.codecManager.getEncoder(); - const decoder = this.codecManager.getDecoder(); - const senderId = generateStringId(); - const channelId = 'opchan-messages'; - - try { - this.channel = await ReliableChannel.create( - node, - channelId, - senderId, - encoder, - decoder - ); - this.setupChannelListeners(this.channel); - } catch (error) { - console.error('Failed to create reliable channel:', error); - } - } - - private setupChannelListeners( - channel: ReliableChannel - ): void { - channel.addEventListener(ReliableChannelEvent.InMessageReceived, event => { - try { - const wakuMessage = event.detail; - if (wakuMessage.payload) { - const opchanMessage = this.codecManager.decodeMessage( - wakuMessage.payload - ); - this.incomingMessageCallbacks.forEach(callback => - callback(opchanMessage) - ); - } - } catch (error) { - console.error('Failed to process incoming message:', error); - } - }); - - channel.addEventListener(ReliableChannelEvent.OutMessageSent, event => { - const messageId = event.detail; - this.messageCallbacks.get(messageId)?.onSent?.(messageId); - }); - - channel.addEventListener( - ReliableChannelEvent.OutMessageAcknowledged, - event => { - const messageId = event.detail; - this.messageCallbacks.get(messageId)?.onAcknowledged?.(messageId); - } - ); - - channel.addEventListener( - ReliableChannelEvent.OutMessageIrrecoverableError, - event => { - const messageId = event.detail.messageId; - const error = event.detail.error; - const callback = this.messageCallbacks.get(messageId); - - if (callback?.onError) { - callback.onError(messageId, error?.toString() || 'Unknown error'); - } - - this.messageCallbacks.delete(messageId); - } - ); - } + // ===== PUBLIC METHODS ===== public async sendMessage( message: OpchanMessage, statusCallback?: MessageStatusCallback - ): Promise { + ) { if (!this.channel) { throw new Error('Reliable channel not initialized'); } @@ -128,4 +62,75 @@ export class ReliableMessaging { this.incomingMessageCallbacks.clear(); this.channel = null; } + + // ===== PRIVATE METHODS ===== + + private async initializeChannel(node: LightNode): Promise { + const encoder = this.codecManager.getEncoder(); + const decoder = this.codecManager.getDecoder(); + const senderId = generateStringId(); + const channelId = 'opchan-messages'; + + try { + this.channel = await ReliableChannel.create( + node, + channelId, + senderId, + encoder, + decoder, + { initialQueryLookbackMs: 30 * 24 * 60 * 60 * 1000 } + ); + this.setupChannelListeners(this.channel); + } catch (error) { + console.error('Failed to create reliable channel:', error); + } + } + + private setupChannelListeners( + channel: ReliableChannel + ): void { + channel.addEventListener("message-received", event => { + try { + const wakuMessage = event.detail; + if (wakuMessage.payload) { + const opchanMessage = this.codecManager.decodeMessage( + wakuMessage.payload + ); + this.incomingMessageCallbacks.forEach(callback => + callback(opchanMessage) + ); + } + } catch (error) { + console.error('Failed to process incoming message:', error); + } + }); + + channel.addEventListener("message-sent", event => { + const messageId = event.detail; + this.messageCallbacks.get(messageId)?.onSent?.(messageId); + }); + + channel.addEventListener( + "message-acknowledged", + event => { + const messageId = event.detail; + this.messageCallbacks.get(messageId)?.onAcknowledged?.(messageId); + } + ); + + channel.addEventListener( + "sending-message-irrecoverable-error", + event => { + const messageId = event.detail.messageId; + const error = event.detail.error; + const callback = this.messageCallbacks.get(messageId); + + if (callback?.onError) { + callback.onError(messageId, error?.toString() || 'Unknown error'); + } + + this.messageCallbacks.delete(messageId); + } + ); + } } diff --git a/src/lib/waku/core/WakuNodeManager.ts b/packages/core/src/lib/waku/core/WakuNodeManager.ts similarity index 95% rename from src/lib/waku/core/WakuNodeManager.ts rename to packages/core/src/lib/waku/core/WakuNodeManager.ts index f5ead36..77acd46 100644 --- a/src/lib/waku/core/WakuNodeManager.ts +++ b/packages/core/src/lib/waku/core/WakuNodeManager.ts @@ -1,4 +1,5 @@ import { createLightNode, LightNode, WakuEvent, HealthStatus } from '@waku/sdk'; +import { environment } from '../../utils/environment'; export type HealthChangeCallback = ( isReady: boolean, @@ -32,9 +33,7 @@ export class WakuNodeManager { const health = event.detail; this._currentHealth = health; - if (import.meta.env?.DEV) { - console.debug(`Waku health status: ${health}`); - } + const wasReady = this._isReady; this._isReady = diff --git a/src/lib/waku/index.ts b/packages/core/src/lib/waku/index.ts similarity index 55% rename from src/lib/waku/index.ts rename to packages/core/src/lib/waku/index.ts index 0d50633..ec7e673 100644 --- a/src/lib/waku/index.ts +++ b/packages/core/src/lib/waku/index.ts @@ -1,5 +1,5 @@ import { HealthStatus } from '@waku/sdk'; -import { OpchanMessage } from '@/types/forum'; +import { OpchanMessage } from '../../types/forum'; import { WakuNodeManager, HealthChangeCallback } from './core/WakuNodeManager'; import { MessageService, @@ -11,18 +11,64 @@ export type { HealthChangeCallback, MessageStatusCallback }; class MessageManager { private nodeManager: WakuNodeManager | null = null; - // LocalDatabase eliminates the need for CacheService private messageService: MessageService | null = null; private reliableMessaging: ReliableMessaging | null = null; constructor() {} + // ===== PUBLIC STATIC METHODS ===== + public static async create(): Promise { const manager = new MessageManager(); await manager.initialize(); return manager; } + // ===== PUBLIC INSTANCE METHODS ===== + + public async stop(): Promise { + this.cleanupReliableMessaging(); + this.messageService?.cleanup(); + await this.nodeManager?.stop(); + } + + public get isReady(): boolean { + return this.nodeManager?.isReady ?? false; + } + + public get currentHealth(): HealthStatus { + return this.nodeManager?.currentHealth ?? HealthStatus.Unhealthy; + } + + public onHealthChange(callback: HealthChangeCallback): () => void { + if (!this.nodeManager) { + throw new Error('Node manager not initialized'); + } + return this.nodeManager.onHealthChange(callback); + } + + //TODO: return event handlers? + public async sendMessage( + message: OpchanMessage, + statusCallback?: MessageStatusCallback + ): Promise { + if (!this.messageService) { + throw new Error('MessageManager not fully initialized'); + } + this.messageService.sendMessage(message, statusCallback); + } + + public onMessageReceived( + callback: (message: OpchanMessage) => void + ): () => void { + if (!this.messageService) { + throw new Error('MessageManager not fully initialized'); + } + return this.messageService.onMessageReceived(callback); + } + + // ===== PRIVATE METHODS ===== + private async initialize(): Promise { try { this.nodeManager = await WakuNodeManager.create(); @@ -72,55 +118,91 @@ class MessageManager { this.messageService?.updateReliableMessaging(null); } } +} - public async stop(): Promise { - this.cleanupReliableMessaging(); - this.messageService?.cleanup(); - await this.nodeManager?.stop(); - } +// Create a default instance that can be used synchronously but initialized asynchronously +export class DefaultMessageManager { + private _instance: MessageManager | null = null; + private _initPromise: Promise | null = null; + private _pendingHealthSubscriptions: HealthChangeCallback[] = []; + private _pendingMessageSubscriptions: ((message: any) => void)[] = []; - public get isReady(): boolean { - return this.nodeManager?.isReady ?? false; - } + // ===== PUBLIC METHODS ===== - public get currentHealth(): HealthStatus { - return this.nodeManager?.currentHealth ?? HealthStatus.Unhealthy; - } - - public onHealthChange(callback: HealthChangeCallback): () => void { - if (!this.nodeManager) { - throw new Error('Node manager not initialized'); + // Initialize the manager asynchronously + async initialize(): Promise { + if (!this._initPromise) { + this._initPromise = MessageManager.create(); } - return this.nodeManager.onHealthChange(callback); + this._instance = await this._initPromise; + + // Establish all pending health subscriptions + this._pendingHealthSubscriptions.forEach(callback => { + this._instance!.onHealthChange(callback); + }); + this._pendingHealthSubscriptions = []; + + // Establish all pending message subscriptions + this._pendingMessageSubscriptions.forEach(callback => { + this._instance!.onMessageReceived(callback); + }); + this._pendingMessageSubscriptions = []; } - //TODO: return event handlers? - public async sendMessage( - message: OpchanMessage, - statusCallback?: MessageStatusCallback - ): Promise { - if (!this.messageService) { - throw new Error('MessageManager not fully initialized'); - } - this.messageService.sendMessage(message, statusCallback); + // Proxy other common methods + get isReady(): boolean { + return this._instance?.isReady ?? false; } - public onMessageReceived( - callback: (message: OpchanMessage) => void - ): () => void { - if (!this.messageService) { - throw new Error('MessageManager not fully initialized'); - } - return this.messageService.onMessageReceived(callback); + get currentHealth() { + return this._instance?.currentHealth; } - public get messageCache() { - if (!this.messageService) { - throw new Error('MessageManager not fully initialized'); + async sendMessage(message: any, callback?: any): Promise { + if (!this._instance) { + await this.initialize(); } - return this.messageService.messageCache; + return this._instance!.sendMessage(message, callback); + } + + onHealthChange(callback: any) { + if (!this._instance) { + // Queue the callback for when we're initialized + this._pendingHealthSubscriptions.push(callback); + + // Return a function that removes from the pending queue + return () => { + const index = this._pendingHealthSubscriptions.indexOf(callback); + if (index !== -1) { + this._pendingHealthSubscriptions.splice(index, 1); + } + }; + } + return this._instance.onHealthChange(callback); + } + + onMessageReceived(callback: any) { + if (!this._instance) { + // Queue the callback for when we're initialized + this._pendingMessageSubscriptions.push(callback); + + // Return a function that removes from the pending queue + return () => { + const index = this._pendingMessageSubscriptions.indexOf(callback); + if (index !== -1) { + this._pendingMessageSubscriptions.splice(index, 1); + } + }; + } + return this._instance.onMessageReceived(callback); } } -const messageManager = await MessageManager.create(); +const messageManager = new DefaultMessageManager(); + +// Initialize in the background +messageManager.initialize().catch(error => { + console.error('Failed to initialize default MessageManager:', error); +}); + export default messageManager; diff --git a/src/lib/waku/network.ts b/packages/core/src/lib/waku/network.ts similarity index 93% rename from src/lib/waku/network.ts rename to packages/core/src/lib/waku/network.ts index 54191a5..0e34cfd 100644 --- a/src/lib/waku/network.ts +++ b/packages/core/src/lib/waku/network.ts @@ -1,6 +1,8 @@ -import messageManager from '@/lib/waku'; +import messageManager from '../waku'; import { HealthStatus } from '@waku/sdk'; +type HealthChangeCallback = (isReady: boolean, health: HealthStatus) => void; + export type ToastFunction = (props: { title: string; description: string; @@ -85,7 +87,7 @@ export const monitorNetworkHealth = ( toast: ToastFunction ): { unsubscribe: () => void } => { setIsNetworkConnected(messageManager.isReady); - const unsubscribe = messageManager.onHealthChange((isReady, health) => { + const unsubscribe = messageManager.onHealthChange((isReady: boolean, health: HealthStatus) => { setIsNetworkConnected(isReady); if (health === HealthStatus.SufficientlyHealthy) { diff --git a/src/lib/waku/services/MessageService.ts b/packages/core/src/lib/waku/services/MessageService.ts similarity index 92% rename from src/lib/waku/services/MessageService.ts rename to packages/core/src/lib/waku/services/MessageService.ts index 50cb61d..f03a54c 100644 --- a/src/lib/waku/services/MessageService.ts +++ b/packages/core/src/lib/waku/services/MessageService.ts @@ -1,10 +1,10 @@ -import { OpchanMessage } from '@/types/forum'; +import { OpchanMessage } from '../../../types/forum'; import { ReliableMessaging, MessageStatusCallback, } from '../core/ReliableMessaging'; import { WakuNodeManager } from '../core/WakuNodeManager'; -import { localDatabase } from '@/lib/database/LocalDatabase'; +import { localDatabase } from '../../database/LocalDatabase'; export type MessageReceivedCallback = (message: OpchanMessage) => void; export type { MessageStatusCallback }; @@ -19,18 +19,7 @@ export class MessageService { this.setupMessageHandling(); } - private setupMessageHandling(): void { - if (this.reliableMessaging) { - this.reliableMessaging.onMessage(async message => { - localDatabase.setSyncing(true); - const isNew = await localDatabase.updateCache(message); - // Defensive: clear pending on inbound message to avoid stuck state - localDatabase.clearPending(message.id); - localDatabase.setSyncing(false); - if (isNew) this.messageReceivedCallbacks.forEach(cb => cb(message)); - }); - } - } + // ===== PUBLIC METHODS ===== public async sendMessage( message: OpchanMessage, @@ -55,6 +44,7 @@ export class MessageService { onSent: id => { console.log(`Message ${id} sent`); statusCallback?.onSent?.(id); + try { localDatabase.clearPending(message.id); } catch {} }, onAcknowledged: id => { console.log(`Message ${id} acknowledged`); @@ -93,12 +83,23 @@ export class MessageService { this.setupMessageHandling(); } - public get messageCache() { - return localDatabase.cache; - } - public cleanup(): void { this.messageReceivedCallbacks.clear(); this.reliableMessaging?.cleanup(); } + + // ===== PRIVATE METHODS ===== + + private setupMessageHandling(): void { + if (this.reliableMessaging) { + this.reliableMessaging.onMessage(async message => { + localDatabase.setSyncing(true); + const isNew = await localDatabase.updateCache(message); + // Defensive: clear pending on inbound message to avoid stuck state + localDatabase.clearPending(message.id); + localDatabase.setSyncing(false); + if (isNew) this.messageReceivedCallbacks.forEach(cb => cb(message)); + }); + } + } } diff --git a/src/lib/wallet/config.ts b/packages/core/src/lib/wallet/config.ts similarity index 100% rename from src/lib/wallet/config.ts rename to packages/core/src/lib/wallet/config.ts diff --git a/src/lib/wallet/index.ts b/packages/core/src/lib/wallet/index.ts similarity index 96% rename from src/lib/wallet/index.ts rename to packages/core/src/lib/wallet/index.ts index 90d0f8b..7241d96 100644 --- a/src/lib/wallet/index.ts +++ b/packages/core/src/lib/wallet/index.ts @@ -1,6 +1,6 @@ import { UseAppKitAccountReturn } from '@reown/appkit/react'; import { AppKit } from '@reown/appkit'; -import { ordinals } from '@/lib/services/Ordinals'; +import { ordinals } from '../services/Ordinals'; import { getEnsName, verifyMessage as verifyEthereumMessage, @@ -39,6 +39,8 @@ export class WalletManager { } } + // ===== PUBLIC STATIC METHODS ===== + /** * Create or get the singleton instance */ @@ -53,6 +55,7 @@ export class WalletManager { bitcoinAccount, ethereumAccount ); + return WalletManager.instance; } @@ -65,6 +68,9 @@ export class WalletManager { 'WalletManager not initialized. Call WalletManager.create() first.' ); } + + + return WalletManager.instance; } @@ -111,6 +117,48 @@ export class WalletManager { } } + /** + * Verify a message signature against a wallet address + * @param message - The original message that was signed + * @param signature - The signature to verify + * @param walletAddress - The expected signer's address + * @param walletType - The type of wallet (bitcoin/ethereum) + * @returns Promise - True if signature is valid + */ + static async verifySignature( + message: string, + signature: string, + walletAddress: string, + walletType: 'bitcoin' | 'ethereum' + ): Promise { + try { + if (walletType === 'ethereum') { + return await verifyEthereumMessage(config, { + address: walletAddress as `0x${string}`, + message, + signature: signature as `0x${string}`, + }); + } else if (walletType === 'bitcoin') { + //TODO: implement bitcoin signature verification + return true; + } + + console.error( + 'WalletManager.verifySignature - unknown wallet type:', + walletType + ); + return false; + } catch (error) { + console.error( + 'WalletManager.verifySignature - error verifying signature:', + error + ); + return false; + } + } + + // ===== PUBLIC INSTANCE METHODS ===== + /** * Get the currently active wallet */ @@ -185,50 +233,6 @@ export class WalletManager { } } - /** - * Verify a message signature against a wallet address - * @param message - The original message that was signed - * @param signature - The signature to verify - * @param walletAddress - The expected signer's address - * @param walletType - The type of wallet (bitcoin/ethereum) - * @returns Promise - True if signature is valid - */ - static async verifySignature( - message: string, - signature: string, - walletAddress: string, - walletType: 'bitcoin' | 'ethereum' - ): Promise { - try { - if (import.meta.env?.DEV) { - // Keep this lightweight in dev; avoid logging full message/signature repeatedly - console.debug('WalletManager.verifySignature', { walletType }); - } - if (walletType === 'ethereum') { - return await verifyEthereumMessage(config, { - address: walletAddress as `0x${string}`, - message, - signature: signature as `0x${string}`, - }); - } else if (walletType === 'bitcoin') { - //TODO: implement bitcoin signature verification - return true; - } - - console.error( - 'WalletManager.verifySignature - unknown wallet type:', - walletType - ); - return false; - } catch (error) { - console.error( - 'WalletManager.verifySignature - error verifying signature:', - error - ); - return false; - } - } - /** * Get comprehensive wallet info including ENS resolution for Ethereum */ diff --git a/src/lib/wallet/types.ts b/packages/core/src/lib/wallet/types.ts similarity index 100% rename from src/lib/wallet/types.ts rename to packages/core/src/lib/wallet/types.ts diff --git a/src/types/forum.ts b/packages/core/src/types/forum.ts similarity index 97% rename from src/types/forum.ts rename to packages/core/src/types/forum.ts index fa0a5f2..b4dbd29 100644 --- a/src/types/forum.ts +++ b/packages/core/src/types/forum.ts @@ -5,9 +5,9 @@ import { VoteMessage, ModerateMessage, UserProfileUpdateMessage, -} from '@/types/waku'; +} from './waku'; import { EVerificationStatus } from './identity'; -import { DelegationProof } from '@/lib/delegation/types'; +import { DelegationProof } from '../lib/delegation/types'; /** * Union type of all message types diff --git a/src/types/identity.ts b/packages/core/src/types/identity.ts similarity index 94% rename from src/types/identity.ts rename to packages/core/src/types/identity.ts index d3f30cc..5341ff7 100644 --- a/src/types/identity.ts +++ b/packages/core/src/types/identity.ts @@ -5,9 +5,9 @@ export type User = { ordinalDetails?: OrdinalDetails; ensDetails?: EnsDetails; - //TODO: implement call sign & display preference setup callSign?: string; displayPreference: EDisplayPreference; + displayName: string; verificationStatus: EVerificationStatus; diff --git a/src/types/index.ts b/packages/core/src/types/index.ts similarity index 100% rename from src/types/index.ts rename to packages/core/src/types/index.ts diff --git a/src/types/waku.ts b/packages/core/src/types/waku.ts similarity index 97% rename from src/types/waku.ts rename to packages/core/src/types/waku.ts index ac1e7c9..a36c6c3 100644 --- a/src/types/waku.ts +++ b/packages/core/src/types/waku.ts @@ -1,5 +1,5 @@ import { EDisplayPreference, EVerificationStatus } from './identity'; -import { DelegationProof } from '@/lib/delegation/types'; +import { DelegationProof } from '../lib/delegation/types'; /** * Message types for Waku communication @@ -139,7 +139,7 @@ export type UnsignedMessage = | UnsignedModerateMessage | UnsignedUserProfileUpdateMessage; -export type SignedMessage = +export type WakuSignedMessage = | CellMessage | PostMessage | CommentMessage @@ -177,5 +177,6 @@ export interface UserIdentityCache { displayPreference: EDisplayPreference; lastUpdated: number; verificationStatus: EVerificationStatus; + displayName: string; }; } diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000..1bc0f86 --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "composite": true, + "tsBuildInfoFile": "./dist/.tsbuildinfo", + "baseUrl": "./src", + "target": "es2020", + "module": "esnext", + "lib": ["es2020", "dom"] + }, + "include": ["src/**/*"], + "exclude": ["dist", "**/*.test.ts", "**/*.spec.ts"] +} diff --git a/packages/react/README.md b/packages/react/README.md new file mode 100644 index 0000000..4b64747 --- /dev/null +++ b/packages/react/README.md @@ -0,0 +1,199 @@ +## @opchan/react + +Lightweight React provider and hooks for building OpChan clients on top of `@opchan/core`. + +### Install + +```bash +npm i @opchan/react @opchan/core react react-dom +``` + +### Quickstart + +#### Basic Usage + +```tsx +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import { OpChanProvider } from '@opchan/react'; +import type { OpChanClientConfig } from '@opchan/core'; + +const config: OpChanClientConfig = { + ordiscanApiKey: 'YOUR_ORDISCAN_API_KEY', +}; + +// Optional: bridge your wallet to OpChan +const walletAdapter = { + getAccount() { + // Return { address, walletType: 'bitcoin' | 'ethereum' } or null + return null; + }, + onChange(cb) { + // Subscribe to wallet changes; return an unsubscribe function + return () => {}; + }, +}; + +function App() { + return ( + + {/* your app */} + + ); +} + +createRoot(document.getElementById('root')!).render(); +``` + +#### (Suggested) With Reown AppKit Integration + +Using Reown AppKit for wallet management: + +```tsx +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import { WagmiProvider } from 'wagmi'; +import { AppKitProvider } from '@reown/appkit/react'; +import { OpchanWithAppKit } from './providers/OpchanWithAppKit'; + +// Define your own config for networks, or use our by default (supports Bitcoin and Ethereum) +import { config, appkitConfig } from '@opchan/core'; + +const opchanConfig = { ordiscanApiKey: 'YOUR_ORDISCAN_API_KEY' }; + +function App() { + return ( + + + + {/* your app */} + + + + ); +} + +createRoot(document.getElementById('root')!).render(); +``` + +### Common usage + +```tsx +import { useForum } from '@opchan/react'; + +export function NewPostButton({ cellId }: { cellId: string }) { + const { user, content, permissions } = useForum(); + + const onCreate = async () => { + if (!permissions.canPost) return; + await content.createPost({ cellId, title: 'Hello', content: 'World' }); + }; + + return ( + + ); +} +``` + +```tsx +import { useAuth, useUserDisplay } from '@opchan/react'; + +export function Connect() { + const { currentUser, connect, disconnect, verifyOwnership } = useAuth(); + const display = useUserDisplay(currentUser?.address ?? ''); + + return ( +
+ {currentUser ? ( + <> + {display.displayName} + + + + ) : ( + + )} +
+ ); +} +``` + +### API + +- **Providers** + - **`OpChanProvider`**: High-level provider that constructs an `OpChanClient` and wires persistence/events. + - Props: + - `config: OpChanClientConfig` — core client configuration. + - `walletAdapter?: WalletAdapter` — optional bridge to your wallet system. + - `children: React.ReactNode`. + - Types: + - `WalletAdapterAccount`: `{ address: string; walletType: 'bitcoin' | 'ethereum' }`. + - `WalletAdapter`: + - `getAccount(): WalletAdapterAccount | null` + - `onChange(cb: (a: WalletAdapterAccount | null) => void): () => void` + - **`OpchanWithAppKit`**: Convenience wrapper around `OpChanProvider` that integrates with Reown AppKit. + - Props: + - `config: OpChanClientConfig` — core client configuration. + - `children: React.ReactNode`. + - Automatically bridges AppKit wallet connections to OpChan's wallet adapter interface. + - Requires `WagmiProvider` and `AppKitProvider` from Reown AppKit as parent providers. + - **`ClientProvider`**: Low-level provider if you construct `OpChanClient` yourself. + - Props: `{ client: OpChanClient; children: React.ReactNode }`. + +- **Hooks** + - **`useForum()`** → `{ user, content, permissions, network }` — convenience bundle of the hooks below. + + - **`useAuth()`** → session & identity actions + - Data: `currentUser`, `verificationStatus`, `isAuthenticated`, `delegationInfo`. + - Actions: `connect({ address, walletType })`, `disconnect()`, `verifyOwnership()`, + `delegate(duration)`, `delegationStatus()`, `clearDelegation()`, + `updateProfile({ callSign?, displayPreference? })`. + + - **`useContent()`** → forum data & actions + - Data: `cells`, `posts`, `comments`, `bookmarks`, `postsByCell`, `commentsByPost`, + `cellsWithStats`, `userVerificationStatus`, `lastSync`, `pending` helpers. + - Actions: `createCell({ name, description, icon? })`, + `createPost({ cellId, title, content })`, + `createComment({ postId, content })`, + `vote({ targetId, isUpvote })`, + `moderate.{post,unpost,comment,uncomment,user,unuser}(...)`, + `togglePostBookmark(post, cellId?)`, `toggleCommentBookmark(comment, postId?)`, + `removeBookmark(bookmarkId)`, `clearAllBookmarks()`, `refresh()`. + + - **`usePermissions()`** → permission checks + - Booleans: `canPost`, `canComment`, `canVote`, `canCreateCell`, `canDelegate`. + - Functions: `canModerate(cellId)`, `check(action, cellId?) → { allowed, reason }`, `reasons`. + + - **`useNetwork()`** → connection state + - Data: `isConnected`, `statusMessage`, `issues`, `canRefresh`. + - Actions: `refresh()` — triggers a light data refresh via core. + + - **`useUIState(key, defaultValue, category?)`** → persisted UI state + - Returns `[value, setValue, { loading, error? }]`. + - Categories: `'wizardStates' | 'preferences' | 'temporaryStates'` (default `'preferences'`). + + - **`useUserDisplay(address)`** → identity details for any address + - Returns `{ address, displayName, callSign?, ensName?, ordinalDetails?, verificationStatus, displayPreference, lastUpdated, isLoading, error }`. + - Backed by a centralized identity cache; updates propagate automatically. + + - **`useClient()`** → access the underlying `OpChanClient` (advanced use only). + +### Notes + +- Identity resolution, verification states, and display preferences are centralized and cached; + `useUserDisplay` and `useAuth.verifyOwnership()` will keep store and local DB in sync. +- This package is UI-agnostic; pair with your component library of choice. + +### License + +MIT + + diff --git a/packages/react/eslint.config.js b/packages/react/eslint.config.js new file mode 100644 index 0000000..900b08f --- /dev/null +++ b/packages/react/eslint.config.js @@ -0,0 +1,27 @@ +import js from '@eslint/js'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['src/**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + parserOptions: { + tsconfigRootDir: new URL('.', import.meta.url).pathname, + }, + }, + rules: { + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-unused-vars': [ + 'error', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, + ], + }, + } +); + + + + diff --git a/packages/react/package.json b/packages/react/package.json new file mode 100644 index 0000000..8a8f8b5 --- /dev/null +++ b/packages/react/package.json @@ -0,0 +1,43 @@ +{ + "name": "@opchan/react", + "version": "1.0.2", + "private": false, + "description": "React contexts and hooks for OpChan built on @opchan/core", + "main": "dist/index.js", + "module": "dist/index.esm.js", + "types": "dist/index.d.ts", + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.esm.js", + "require": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "npm run clean && npm run build:esm && npm run build:types", + "build:esm": "tsc -b --force tsconfig.build.json && cp dist/index.js dist/index.esm.js", + "build:types": "tsc -p tsconfig.types.json", + "clean": "rm -rf dist", + "dev": "tsc -w -p tsconfig.build.json", + "lint": "eslint src --ext .ts,.tsx" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + }, + "dependencies": { + "@opchan/core": "file:../core" + }, + "devDependencies": { + "typescript": "^5.5.3", + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22" + } +} diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts new file mode 100644 index 0000000..a88c4fe --- /dev/null +++ b/packages/react/src/index.ts @@ -0,0 +1,19 @@ + + +export { + ClientProvider, + useClient, +} from './v1/context/ClientContext'; + +export { OpChanProvider } from './v1/provider/OpChanProvider'; +export type { WalletAdapter, WalletAdapterAccount } from './v1/provider/OpChanProvider'; + +export { useAuth } from './v1/hooks/useAuth'; +export { useContent } from './v1/hooks/useContent'; +export { usePermissions } from './v1/hooks/usePermissions'; +export { useNetwork } from './v1/hooks/useNetwork'; +export { useForum } from './v1/hooks/useForum'; +export { useUIState } from './v1/hooks/useUIState'; +export { useUserDisplay } from './v1/hooks/useUserDisplay'; + + diff --git a/packages/react/src/v1/context/ClientContext.tsx b/packages/react/src/v1/context/ClientContext.tsx new file mode 100644 index 0000000..d81bae5 --- /dev/null +++ b/packages/react/src/v1/context/ClientContext.tsx @@ -0,0 +1,28 @@ +import React, { createContext, useContext } from 'react'; +import type { OpChanClient } from '@opchan/core'; + +interface ClientContextValue { + client: OpChanClient; +} + +const ClientContext = createContext(null); + +type ProviderProps = { client: OpChanClient; children: React.ReactNode }; + +export function ClientProvider({ client, children }: ProviderProps) { + return ( + + {children} + + ); +} + +export function useClient(): OpChanClient { + const ctx = useContext(ClientContext); + if (!ctx) throw new Error('useClient must be used within ClientProvider'); + return ctx.client; +} + + + + diff --git a/packages/react/src/v1/hooks/useAuth.ts b/packages/react/src/v1/hooks/useAuth.ts new file mode 100644 index 0000000..896587a --- /dev/null +++ b/packages/react/src/v1/hooks/useAuth.ts @@ -0,0 +1,209 @@ +import React from 'react'; +import { useClient } from '../context/ClientContext'; +import { useOpchanStore, setOpchanState } from '../store/opchanStore'; +import { + User, + EVerificationStatus, + DelegationDuration, + EDisplayPreference, + walletManager, +} from '@opchan/core'; +import type { DelegationFullStatus } from '@opchan/core'; + +export interface ConnectInput { + address: string; + walletType: 'bitcoin' | 'ethereum'; +} + +export function useAuth() { + const client = useClient(); + const currentUser = useOpchanStore(s => s.session.currentUser); + const verificationStatus = useOpchanStore(s => s.session.verificationStatus); + const delegation = useOpchanStore(s => s.session.delegation); + + + const connect = React.useCallback(async (input: ConnectInput): Promise => { + const baseUser: User = { + address: input.address, + walletType: input.walletType, + displayName: input.address.slice(0, 6) + '...' + input.address.slice(-4), + displayPreference: EDisplayPreference.WALLET_ADDRESS, + verificationStatus: EVerificationStatus.WALLET_CONNECTED, + lastChecked: Date.now(), + }; + + try { + await client.database.storeUser(baseUser); + // Prime identity service so display name/ens are cached + const identity = await client.userIdentityService.getIdentity(baseUser.address); + if (!identity) return false; + setOpchanState(prev => ({ + ...prev, + session: { + ...prev.session, + currentUser: { + ...baseUser, + ...identity, + }, + }, + })); + return true; + } catch (e) { + console.error('connect failed', e); + return false; + } + }, [client]); + + const disconnect = React.useCallback(async (): Promise => { + try { + await client.database.clearUser(); + } finally { + setOpchanState(prev => ({ + ...prev, + session: { + currentUser: null, + verificationStatus: EVerificationStatus.WALLET_UNCONNECTED, + delegation: null, + }, + })); + } + }, [client]); + + const verifyOwnership = React.useCallback(async (): Promise => { + console.log('verifyOwnership') + const user = currentUser; + if (!user) return false; + try { + const identity = await client.userIdentityService.getIdentity(user.address, { fresh: true }); + if (!identity) { + console.error('verifyOwnership failed', 'identity not found'); + return false; + } + + console.log({user, identity}) + + const updated: User = { + ...user, + ...identity, + }; + + await client.database.storeUser(updated); + await client.database.upsertUserIdentity(user.address, { + displayName: identity.displayName, + ensName: identity?.ensName || undefined, + ordinalDetails: identity?.ordinalDetails, + verificationStatus: identity.verificationStatus, + lastUpdated: Date.now(), + }); + + setOpchanState(prev => ({ + ...prev, + session: { ...prev.session, currentUser: updated, verificationStatus: identity.verificationStatus }, + })); + return identity.verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED; + } catch (e) { + console.error('verifyOwnership failed', e); + return false; + } + }, [client, currentUser]); + + const delegate = React.useCallback(async ( + duration: DelegationDuration = '7days', + ): Promise => { + const user = currentUser; + if (!user) return false; + try { + const signer = ((message: string) => walletManager.getInstance().signMessage(message)); + const ok = await client.delegation.delegate( + user.address, + user.walletType, + duration, + signer, + ); + + const status = await client.delegation.getStatus(user.address, user.walletType); + setOpchanState(prev => ({ + ...prev, + session: { ...prev.session, delegation: status }, + })); + return ok; + } catch (e) { + console.error('delegate failed', e); + return false; + } + }, [client, currentUser]); + + const delegationStatus = React.useCallback(async () => { + const user = currentUser; + if (!user) return { hasDelegation: false, isValid: false } as const; + return client.delegation.getStatus(user.address, user.walletType); + }, [client, currentUser]); + + const clearDelegation = React.useCallback(async (): Promise => { + try { + await client.delegation.clear(); + setOpchanState(prev => ({ + ...prev, + session: { ...prev.session, delegation: null }, + })); + return true; + } catch (e) { + console.error('clearDelegation failed', e); + return false; + } + }, [client]); + + const updateProfile = React.useCallback(async (updates: { callSign?: string; displayPreference?: EDisplayPreference }): Promise => { + const user = currentUser; + if (!user) return false; + try { + const res = await client.userIdentityService.updateProfile( + user.address, + { + callSign: updates.callSign, + displayPreference: updates.displayPreference ?? user.displayPreference, + } + ); + if (!res.ok) return false; + + const identity = res.identity; + const updated: User = { + ...user, + ...identity, + }; + await client.database.storeUser(updated); + setOpchanState(prev => ({ ...prev, session: { ...prev.session, currentUser: updated } })); + return true; + } catch (e) { + console.error('updateProfile failed', e); + return false; + } + }, [client, currentUser]); + + const delegationInfo = React.useMemo(() => { + const base: DelegationFullStatus = + delegation ?? ({ hasDelegation: false, isValid: false } as const); + const expiresAt = base?.proof?.expiryTimestamp + ? new Date(base.proof.expiryTimestamp) + : undefined; + return { ...base, expiresAt }; + }, [delegation]); + + return { + currentUser, + verificationStatus, + isAuthenticated: currentUser !== null, + delegationInfo, + connect, + disconnect, + verifyOwnership, + delegate, + delegationStatus, + clearDelegation, + updateProfile, + } as const; +} + + + + diff --git a/packages/react/src/v1/hooks/useContent.ts b/packages/react/src/v1/hooks/useContent.ts new file mode 100644 index 0000000..791e42c --- /dev/null +++ b/packages/react/src/v1/hooks/useContent.ts @@ -0,0 +1,310 @@ +import React from 'react'; +import { useClient } from '../context/ClientContext'; +import { useOpchanStore, setOpchanState } from '../store/opchanStore'; +import { + Post, + Comment, + Cell, + EVerificationStatus, + UserVerificationStatus, + BookmarkType, + getDataFromCache, +} from '@opchan/core'; +import { BookmarkService } from '@opchan/core'; + +function reflectCache(client: ReturnType): void { + getDataFromCache().then(({ cells, posts, comments }) => { + setOpchanState(prev => ({ + ...prev, + content: { + ...prev.content, + cells, + posts, + comments, + bookmarks: Object.values(client.database.cache.bookmarks), + lastSync: client.database.getSyncState().lastSync, + pendingIds: prev.content.pendingIds, + pendingVotes: prev.content.pendingVotes, + }, + })); + }).catch(err => { + console.error('reflectCache failed', err); + }); +} + +export function useContent() { + const client = useClient(); + const content = useOpchanStore(s => s.content); + const session = useOpchanStore(s => s.session); + + // Re-render on pending changes from LocalDatabase so isPending reflects current state + const [, forceRender] = React.useReducer((x: number) => x + 1, 0); + React.useEffect(() => { + const off = client.database.onPendingChange(() => { + forceRender(); + }); + return () => { + try { + off(); + } catch (err) { + console.error('Error cleaning up pending change listener:', err); + } + }; + }, [client]); + + // Derived maps + const postsByCell = React.useMemo(() => { + const map: Record = {}; + for (const p of content.posts) { + (map[p.cellId] ||= []).push(p); + } + return map; + }, [content.posts]); + + const commentsByPost = React.useMemo(() => { + const map: Record = {}; + for (const c of content.comments) { + (map[c.postId] ||= []).push(c); + } + for (const postId in map) { + map[postId].sort((a, b) => a.timestamp - b.timestamp); + } + return map; + }, [content.comments]); + + // Derived: user verification status from identity cache + const userVerificationStatus: UserVerificationStatus = React.useMemo(() => { + const identities = client.database.cache.userIdentities; + const result: UserVerificationStatus = {}; + for (const [address, rec] of Object.entries(identities)) { + const hasEns = Boolean(rec.ensName); + const hasOrdinal = Boolean(rec.ordinalDetails); + const isVerified = rec.verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED; + result[address] = { + isVerified, + hasENS: hasEns, + hasOrdinal, + ensName: rec.ensName, + verificationStatus: rec.verificationStatus, + }; + } + return result; + }, [client.database.cache.userIdentities]); + + // Derived: cells with stats for sidebar/trending + const cellsWithStats = React.useMemo(() => { + const byCell: Record; recentActivity: number }> = {}; + const now = Date.now(); + const recentWindowMs = 7 * 24 * 60 * 60 * 1000; // 7 days + for (const p of content.posts) { + const entry = (byCell[p.cellId] ||= { postCount: 0, activeUsers: new Set(), recentActivity: 0 }); + entry.postCount += 1; + entry.activeUsers.add(p.author); + if (now - p.timestamp <= recentWindowMs) entry.recentActivity += 1; + } + for (const c of content.comments) { + // find post for cell reference + const post = content.posts.find(pp => pp.id === c.postId); + if (!post) continue; + const entry = (byCell[post.cellId] ||= { postCount: 0, activeUsers: new Set(), recentActivity: 0 }); + entry.activeUsers.add(c.author); + if (now - c.timestamp <= recentWindowMs) entry.recentActivity += 1; + } + return content.cells.map(cell => { + const stats = byCell[cell.id] || { postCount: 0, activeUsers: new Set(), recentActivity: 0 }; + return { + ...cell, + postCount: stats.postCount, + activeUsers: stats.activeUsers.size, + recentActivity: stats.recentActivity, + } as Cell & { postCount: number; activeUsers: number; recentActivity: number }; + }); + }, [content.cells, content.posts, content.comments]); + + // Actions + const createCell = React.useCallback(async (input: { name: string; description: string; icon?: string }): Promise => { + const currentUser = session.currentUser; + const isAuthenticated = Boolean(currentUser); + const result = await client.forumActions.createCell( + { ...input, currentUser, isAuthenticated }, + () => reflectCache(client) + ); + reflectCache(client); + return result.data ?? null; + }, [client, session.currentUser]); + + const createPost = React.useCallback(async (input: { cellId: string; title: string; content: string }): Promise => { + const currentUser = session.currentUser; + const isAuthenticated = Boolean(currentUser); + const result = await client.forumActions.createPost( + { ...input, currentUser, isAuthenticated }, + () => reflectCache(client) + ); + reflectCache(client); + return result.data ?? null; + }, [client, session.currentUser]); + + const createComment = React.useCallback(async (input: { postId: string; content: string }): Promise => { + const currentUser = session.currentUser; + const isAuthenticated = Boolean(currentUser); + const result = await client.forumActions.createComment( + { ...input, currentUser, isAuthenticated }, + () => reflectCache(client) + ); + reflectCache(client); + return result.data ?? null; + }, [client, session.currentUser]); + + const vote = React.useCallback(async (input: { targetId: string; isUpvote: boolean }): Promise => { + const currentUser = session.currentUser; + const isAuthenticated = Boolean(currentUser); + const result = await client.forumActions.vote( + { ...input, currentUser, isAuthenticated }, + () => reflectCache(client) + ); + reflectCache(client); + return result.data ?? false; + }, [client, session.currentUser]); + + const moderate = React.useMemo(() => ({ + post: async (cellId: string, postId: string, reason?: string) => { + const currentUser = session.currentUser; + const isAuthenticated = Boolean(currentUser); + const cell = content.cells.find(c => c.id === cellId); + const res = await client.forumActions.moderatePost( + { cellId, postId, reason, currentUser, isAuthenticated, cellOwner: cell?.author ?? '' }, + () => reflectCache(client) + ); + reflectCache(client); + return res.data ?? false; + }, + unpost: async (cellId: string, postId: string, reason?: string) => { + const currentUser = session.currentUser; + const isAuthenticated = Boolean(currentUser); + const cell = content.cells.find(c => c.id === cellId); + const res = await client.forumActions.unmoderatePost( + { cellId, postId, reason, currentUser, isAuthenticated, cellOwner: cell?.author ?? '' }, + () => reflectCache(client) + ); + reflectCache(client); + return res.data ?? false; + }, + comment: async (cellId: string, commentId: string, reason?: string) => { + const currentUser = session.currentUser; + const isAuthenticated = Boolean(currentUser); + const cell = content.cells.find(c => c.id === cellId); + const res = await client.forumActions.moderateComment( + { cellId, commentId, reason, currentUser, isAuthenticated, cellOwner: cell?.author ?? '' }, + () => reflectCache(client) + ); + reflectCache(client); + return res.data ?? false; + }, + uncomment: async (cellId: string, commentId: string, reason?: string) => { + const currentUser = session.currentUser; + const isAuthenticated = Boolean(currentUser); + const cell = content.cells.find(c => c.id === cellId); + const res = await client.forumActions.unmoderateComment( + { cellId, commentId, reason, currentUser, isAuthenticated, cellOwner: cell?.author ?? '' }, + () => reflectCache(client) + ); + reflectCache(client); + return res.data ?? false; + }, + user: async (cellId: string, userAddress: string, reason?: string) => { + const currentUser = session.currentUser; + const isAuthenticated = Boolean(currentUser); + const cell = content.cells.find(c => c.id === cellId); + const res = await client.forumActions.moderateUser( + { cellId, userAddress, reason, currentUser, isAuthenticated, cellOwner: cell?.author ?? '' }, + () => reflectCache(client) + ); + reflectCache(client); + return res.data ?? false; + }, + unuser: async (cellId: string, userAddress: string, reason?: string) => { + const currentUser = session.currentUser; + const isAuthenticated = Boolean(currentUser); + const cell = content.cells.find(c => c.id === cellId); + const res = await client.forumActions.unmoderateUser( + { cellId, userAddress, reason, currentUser, isAuthenticated, cellOwner: cell?.author ?? '' }, + () => reflectCache(client) + ); + reflectCache(client); + return res.data ?? false; + }, + }), [client, session.currentUser, content.cells]); + + const togglePostBookmark = React.useCallback(async (post: Post, cellId?: string): Promise => { + const address = session.currentUser?.address; + if (!address) return false; + const added = await BookmarkService.togglePostBookmark(post, address, cellId); + const updated = await client.database.getUserBookmarks(address); + setOpchanState(prev => ({ ...prev, content: { ...prev.content, bookmarks: updated } })); + return added; + }, [client, session.currentUser?.address]); + + const toggleCommentBookmark = React.useCallback(async (comment: Comment, postId?: string): Promise => { + const address = session.currentUser?.address; + if (!address) return false; + const added = await BookmarkService.toggleCommentBookmark(comment, address, postId); + const updated = await client.database.getUserBookmarks(address); + setOpchanState(prev => ({ ...prev, content: { ...prev.content, bookmarks: updated } })); + return added; + }, [client, session.currentUser?.address]); + + const removeBookmark = React.useCallback(async (bookmarkId: string): Promise => { + const address = session.currentUser?.address; + if (!address) return; + const [typeStr, targetId] = bookmarkId.split(':'); + const type = typeStr === 'post' ? BookmarkType.POST : BookmarkType.COMMENT; + await BookmarkService.removeBookmark(type, targetId); + const updated = await client.database.getUserBookmarks(address); + setOpchanState(prev => ({ ...prev, content: { ...prev.content, bookmarks: updated } })); + }, [client, session.currentUser?.address]); + + const clearAllBookmarks = React.useCallback(async (): Promise => { + const address = session.currentUser?.address; + if (!address) return; + await BookmarkService.clearUserBookmarks(address); + const updated = await client.database.getUserBookmarks(address); + setOpchanState(prev => ({ ...prev, content: { ...prev.content, bookmarks: updated } })); + }, [client, session.currentUser?.address]); + + const refresh = React.useCallback(async () => { + // Minimal refresh: re-reflect cache; network refresh is via useNetwork + reflectCache(client); + }, [client]); + + return { + // data + cells: content.cells, + posts: content.posts, + comments: content.comments, + bookmarks: content.bookmarks, + postsByCell, + commentsByPost, + cellsWithStats, + userVerificationStatus, + pending: { + isPending: (id?: string) => (id ? client.database.isPending(id) : false), + onChange: (cb: () => void) => client.database.onPendingChange(cb), + }, + lastSync: content.lastSync, + // actions + createCell, + createPost, + createComment, + vote, + moderate, + togglePostBookmark, + toggleCommentBookmark, + removeBookmark, + clearAllBookmarks, + refresh, + } as const; +} + + + + diff --git a/packages/react/src/v1/hooks/useForum.ts b/packages/react/src/v1/hooks/useForum.ts new file mode 100644 index 0000000..65e760b --- /dev/null +++ b/packages/react/src/v1/hooks/useForum.ts @@ -0,0 +1,16 @@ +import { useAuth } from './useAuth'; +import { useContent } from './useContent'; +import { usePermissions } from './usePermissions'; +import { useNetwork } from './useNetwork'; + +export function useForum() { + const user = useAuth(); + const content = useContent(); + const permissions = usePermissions(); + const network = useNetwork(); + return { user, content, permissions, network } as const; +} + + + + diff --git a/packages/react/src/v1/hooks/useNetwork.ts b/packages/react/src/v1/hooks/useNetwork.ts new file mode 100644 index 0000000..82ce639 --- /dev/null +++ b/packages/react/src/v1/hooks/useNetwork.ts @@ -0,0 +1,29 @@ +import { useOpchanStore } from '../store/opchanStore'; +import { useClient } from '../context/ClientContext'; + +export function useNetwork() { + const client = useClient(); + const network = useOpchanStore(s => s.network); + + const refresh = async () => { + try { + // trigger a database refresh using core helper + const { refreshData } = await import('@opchan/core'); + await refreshData(client.messageManager.isReady, () => {}, () => {}, () => {}); + } catch (e) { + console.error('Network refresh failed', e); + } + }; + + return { + isConnected: network.isConnected, + statusMessage: network.statusMessage, + issues: network.issues, + canRefresh: true, + refresh, + } as const; +} + + + + diff --git a/packages/react/src/v1/hooks/usePermissions.ts b/packages/react/src/v1/hooks/usePermissions.ts new file mode 100644 index 0000000..50448dd --- /dev/null +++ b/packages/react/src/v1/hooks/usePermissions.ts @@ -0,0 +1,69 @@ +import { useOpchanStore } from '../store/opchanStore'; +import { EVerificationStatus } from '@opchan/core'; + +export function usePermissions() { + const { session, content } = useOpchanStore(s => ({ session: s.session, content: s.content })); + const currentUser = session.currentUser; + + const isVerified = session.verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED; + const isConnected = session.verificationStatus !== EVerificationStatus.WALLET_UNCONNECTED; + + const canCreateCell = isVerified || isConnected; + const canPost = isConnected; + const canComment = isConnected; + const canVote = isConnected; + + const canModerate = (cellId: string): boolean => { + if (!currentUser) return false; + const cell = content.cells.find(c => c.id === cellId); + return cell ? cell.author === currentUser.address : false; + }; + + const reasons = { + post: canPost ? '' : 'Connect your wallet to post', + comment: canComment ? '' : 'Connect your wallet to comment', + vote: canVote ? '' : 'Connect your wallet to vote', + createCell: canCreateCell ? '' : 'Verification required to create a cell', + moderate: (cellId: string) => (canModerate(cellId) ? '' : 'Only cell owner can moderate'), + } as const; + + const check = ( + action: + | 'canPost' + | 'canComment' + | 'canVote' + | 'canCreateCell' + | 'canModerate', + cellId?: string + ): { allowed: boolean; reason: string } => { + switch (action) { + case 'canPost': + return { allowed: canPost, reason: reasons.post }; + case 'canComment': + return { allowed: canComment, reason: reasons.comment }; + case 'canVote': + return { allowed: canVote, reason: reasons.vote }; + case 'canCreateCell': + return { allowed: canCreateCell, reason: reasons.createCell }; + case 'canModerate': + return { allowed: cellId ? canModerate(cellId) : false, reason: cellId ? reasons.moderate(cellId) : 'Cell required' }; + default: + return { allowed: false, reason: 'Unknown action' }; + } + }; + + return { + canPost, + canComment, + canVote, + canCreateCell, + canDelegate: isVerified || isConnected, + canModerate, + reasons, + check, + } as const; +} + + + + diff --git a/packages/react/src/v1/hooks/useUIState.ts b/packages/react/src/v1/hooks/useUIState.ts new file mode 100644 index 0000000..6edc960 --- /dev/null +++ b/packages/react/src/v1/hooks/useUIState.ts @@ -0,0 +1,68 @@ +import React from 'react'; +import { useClient } from '../context/ClientContext'; +import { useOpchanStore, opchanStore } from '../store/opchanStore'; + +export function useUIState(key: string, defaultValue: T, category: 'wizardStates' | 'preferences' | 'temporaryStates' = 'preferences'): [T, (value: T) => void, { loading: boolean; error?: string }] { + const client = useClient(); + + // Get value from central store + const storeValue = useOpchanStore(s => s.uiState[category][key] as T | undefined); + + const [loading, setLoading] = React.useState(true); + const [error, setError] = React.useState(undefined); + const [hasHydrated, setHasHydrated] = React.useState(false); + + // Hydrate from LocalDatabase on first load (if not already in store) + React.useEffect(() => { + if (hasHydrated) return; + + let mounted = true; + (async () => { + try { + // Check if already in store + if (storeValue !== undefined) { + if (mounted) { + setLoading(false); + setHasHydrated(true); + } + return; + } + + // Load from LocalDatabase and populate store + const dbKey = category === 'wizardStates' ? `wizard_${key}` : + category === 'preferences' ? `pref_${key}` : key; + const value = await client.database.loadUIState(dbKey); + + if (mounted) { + if (value !== undefined) { + opchanStore.setUIState(key, value as T, category); + } + setLoading(false); + setHasHydrated(true); + } + } catch (e) { + if (mounted) { + setError((e as Error).message); + setLoading(false); + setHasHydrated(true); + } + } + })(); + + return () => { + mounted = false; + }; + }, [client, key, category, storeValue, hasHydrated]); + + const set = React.useCallback((value: T) => { + // Update store (will auto-persist via StoreWiring) + opchanStore.setUIState(key, value, category); + }, [key, category]); + + // Use store value if available, otherwise default + const currentValue = storeValue !== undefined ? storeValue : defaultValue; + + return [currentValue, set, { loading, error }]; +} + + diff --git a/packages/react/src/v1/hooks/useUserDisplay.ts b/packages/react/src/v1/hooks/useUserDisplay.ts new file mode 100644 index 0000000..cf6653d --- /dev/null +++ b/packages/react/src/v1/hooks/useUserDisplay.ts @@ -0,0 +1,97 @@ +import React from 'react'; +import { useClient } from '../context/ClientContext'; +import { useOpchanStore, opchanStore } from '../store/opchanStore'; +import { EDisplayPreference, EVerificationStatus } from '@opchan/core'; +import { UserIdentity } from '@opchan/core/dist/lib/services/UserIdentityService'; + +export type UserDisplayInfo = UserIdentity & { + isLoading: boolean; + error: string | null; +} + +/** + * User display hook with caching and reactive updates + * Takes an address and resolves display details for it + */ +export function useUserDisplay(address: string): UserDisplayInfo { + const client = useClient(); + + // Get identity from central store + const storeIdentity = useOpchanStore(s => s.identity.identitiesByAddress[address]); + + const [isLoading, setIsLoading] = React.useState(true); + const [error, setError] = React.useState(null); + const [hasInitialized, setHasInitialized] = React.useState(false); + + // Initialize from store or load from service + React.useEffect(() => { + if (!address || hasInitialized) return; + + let cancelled = false; + + const initializeUserDisplay = async () => { + try { + // If already in store, use that + if (storeIdentity) { + setIsLoading(false); + setError(null); + setHasInitialized(true); + return; + } + + const identity = await client.userIdentityService.getIdentity(address, {fresh: true}); + console.log({identity}) + + if (cancelled) return; + + if (identity) { + opchanStore.setIdentity(address, identity); + setError(null); + } else { + setError(null); + } + + setIsLoading(false); + setHasInitialized(true); + } catch (error) { + if (cancelled) return; + + setIsLoading(false); + setError(error instanceof Error ? error.message : 'Unknown error'); + setHasInitialized(true); + } + }; + + initializeUserDisplay(); + + return () => { + cancelled = true; + }; + }, [address, client.userIdentityService, storeIdentity, hasInitialized]); + + const displayInfo: UserDisplayInfo = React.useMemo(() => { + if (storeIdentity) { + console.log({storeIdentity}) + return { + ...storeIdentity, + isLoading, + error, + }; + } + + return { + address, + displayName: `${address.slice(0, 6)}...${address.slice(-4)}`, + lastUpdated: 0, + callSign: undefined, + ensName: undefined, + ordinalDetails: undefined, + verificationStatus: EVerificationStatus.WALLET_UNCONNECTED, + displayPreference: EDisplayPreference.WALLET_ADDRESS, + isLoading, + error, + }; + }, [storeIdentity, isLoading, error, address]); + + return displayInfo; +} \ No newline at end of file diff --git a/packages/react/src/v1/provider/OpChanProvider.tsx b/packages/react/src/v1/provider/OpChanProvider.tsx new file mode 100644 index 0000000..a52f645 --- /dev/null +++ b/packages/react/src/v1/provider/OpChanProvider.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { OpChanClient, type OpChanClientConfig } from '@opchan/core'; +import { ClientProvider } from '../context/ClientContext'; +import { StoreWiring } from './StoreWiring'; +import { setOpchanState } from '../store/opchanStore'; +import { EVerificationStatus } from '@opchan/core'; +import type { EDisplayPreference, User } from '@opchan/core'; + +export interface WalletAdapterAccount { + address: string; + walletType: 'bitcoin' | 'ethereum'; +} + +export interface WalletAdapter { + getAccount(): WalletAdapterAccount | null; + onChange(callback: (account: WalletAdapterAccount | null) => void): () => void; +} + +export interface NewOpChanProviderProps { + config: OpChanClientConfig; + walletAdapter?: WalletAdapter; + children: React.ReactNode; +} + +/** + * New provider that constructs the OpChanClient and sets up DI. + * Event wiring and store hydration will be handled in a separate effect layer. + */ +export const OpChanProvider: React.FC = ({ config, walletAdapter, children }) => { + const [client] = React.useState(() => new OpChanClient(config)); + + // Bridge wallet adapter to session state + React.useEffect(() => { + if (!walletAdapter) return; + + const syncFromAdapter = async (account: WalletAdapterAccount | null) => { + if (account) { + // Persist base user and update session + const baseUser: User = { + address: account.address, + walletType: account.walletType, + displayPreference: 'wallet-address' as EDisplayPreference, + verificationStatus: EVerificationStatus.WALLET_CONNECTED, + displayName: account.address.slice(0, 6) + '...' + account.address.slice(-4), + lastChecked: Date.now(), + }; + try { + await client.database.storeUser(baseUser); + } catch (err) { + console.warn('OpChanProvider: failed to persist base user', err); + } + setOpchanState(prev => ({ + ...prev, + session: { + currentUser: baseUser, + verificationStatus: baseUser.verificationStatus, + delegation: prev.session.delegation, + }, + })); + } else { + // Clear session on disconnect + try { await client.database.clearUser(); } catch (err) { + console.warn('OpChanProvider: failed to clear user on disconnect', err); + } + setOpchanState(prev => ({ + ...prev, + session: { + currentUser: null, + verificationStatus: EVerificationStatus.WALLET_UNCONNECTED, + delegation: null, + }, + })); + } + }; + + // Initial sync + syncFromAdapter(walletAdapter.getAccount()); + // Subscribe + const off = walletAdapter.onChange(syncFromAdapter); + return () => { try { off(); } catch { /* noop */ } }; + }, [walletAdapter, client]); + + return ( + + + {children} + + ); +}; + + diff --git a/packages/react/src/v1/provider/StoreWiring.tsx b/packages/react/src/v1/provider/StoreWiring.tsx new file mode 100644 index 0000000..20bb403 --- /dev/null +++ b/packages/react/src/v1/provider/StoreWiring.tsx @@ -0,0 +1,218 @@ +import React from 'react'; +import { useClient } from '../context/ClientContext'; +import { setOpchanState, getOpchanState, opchanStore } from '../store/opchanStore'; +import type { OpchanMessage, User, UserIdentity } from '@opchan/core'; +import { EVerificationStatus, getDataFromCache } from '@opchan/core'; + +export const StoreWiring: React.FC = () => { + const client = useClient(); + + // Initial hydrate from LocalDatabase + React.useEffect(() => { + let unsubHealth: (() => void) | null = null; + let unsubMessages: (() => void) | null = null; + let unsubIdentity: (() => void) | null = null; + let unsubPersistence: (() => void) | null = null; + + const hydrate = async () => { + try { + await client.database.open(); + const { cells, posts, comments } = await getDataFromCache(); + + // Reflect transformed content + setOpchanState(prev => ({ + ...prev, + content: { + ...prev.content, + cells, + posts, + comments, + bookmarks: Object.values(client.database.cache.bookmarks), + lastSync: client.database.getSyncState().lastSync, + pendingIds: new Set(), + pendingVotes: new Set(), + }, + })); + + // Hydrate identity cache from LocalDatabase using UserIdentityService + const allIdentities = client.userIdentityService.getAll(); + const identityUpdates: Record = {}; + const displayNameUpdates: Record = {}; + const lastUpdatedMap: Record = {}; + + for (const identity of allIdentities) { + identityUpdates[identity.address] = identity; + displayNameUpdates[identity.address] = identity.displayName; + lastUpdatedMap[identity.address] = identity.lastUpdated; + } + + setOpchanState(prev => ({ + ...prev, + identity: { + ...prev.identity, + identitiesByAddress: identityUpdates, + displayNameByAddress: displayNameUpdates, + lastUpdatedByAddress: lastUpdatedMap, + }, + })); + + // Hydrate session (user + delegation) from LocalDatabase + try { + const loadedUser = await client.database.loadUser(); + const delegationStatus = await client.delegation.getStatus( + loadedUser?.address, + loadedUser?.walletType, + ); + + setOpchanState(prev => ({ + ...prev, + session: { + currentUser: loadedUser, + verificationStatus: + loadedUser?.verificationStatus ?? EVerificationStatus.WALLET_UNCONNECTED, + delegation: delegationStatus ?? null, + }, + })); + } catch (sessionErr) { + console.error('Initial session hydrate failed', sessionErr); + } + } catch (e) { + console.error('Initial hydrate failed', e); + } + }; + + const wire = () => { + unsubHealth = client.messageManager.onHealthChange((isReady: boolean) => { + setOpchanState(prev => ({ + ...prev, + network: { + ...prev.network, + isConnected: isReady, + statusMessage: isReady ? 'connected' : 'connecting…', + issues: isReady ? [] : prev.network.issues, + }, + })); + }); + + unsubMessages = client.messageManager.onMessageReceived(async (message: OpchanMessage) => { + // Persist, then reflect cache in store + try { + await client.database.updateCache(message); + const { cells, posts, comments } = await getDataFromCache(); + setOpchanState(prev => ({ + ...prev, + content: { + ...prev.content, + cells, + posts, + comments, + bookmarks: Object.values(client.database.cache.bookmarks), + lastSync: Date.now(), + pendingIds: prev.content.pendingIds, + pendingVotes: prev.content.pendingVotes, + }, + })); + } catch (e) { + console.error('Failed to apply incoming message', e); + } + }); + + // Reactively update ALL identities when they refresh (not just current user) + unsubIdentity = client.userIdentityService.subscribe(async (address: string, identity) => { + try { + if (!identity) { + // Try to fetch if not provided + identity = await client.userIdentityService.getIdentity(address); + if (!identity) { + return; + } + } + + // Update identity store for ALL users + opchanStore.setIdentity(address, identity); + + // Special handling for current user - also update session + const { session } = getOpchanState(); + const active = session.currentUser; + if (active && active.address === address) { + const updated: User = { + ...active, + ...identity, + }; + + try { + await client.database.storeUser(updated); + } catch (persistErr) { + console.warn('[StoreWiring] Failed to persist updated user after identity refresh:', persistErr); + } + + setOpchanState(prev => ({ + ...prev, + session: { + ...prev.session, + currentUser: updated, + verificationStatus: updated.verificationStatus, + }, + })); + } + + const { cells, posts, comments } = await getDataFromCache(); + setOpchanState(prev => ({ + ...prev, + content: { + ...prev.content, + cells, + posts, + comments, + bookmarks: Object.values(client.database.cache.bookmarks), + lastSync: Date.now(), + pendingIds: prev.content.pendingIds, + pendingVotes: prev.content.pendingVotes, + }, + })); + } catch (err) { + console.warn('Identity refresh wiring failed', err); + } + }); + }; + + hydrate().then(() => { + wire(); + + // Set up bidirectional persistence: Store → LocalDatabase + unsubPersistence = opchanStore.onPersistence(async (state) => { + try { + // Persist current user changes + if (state.session.currentUser) { + await client.database.storeUser(state.session.currentUser); + } + + // Persist UI state changes (wizard flags, preferences) + for (const [key, value] of Object.entries(state.uiState.wizardStates)) { + await client.database.storeUIState(`wizard_${key}`, value); + } + + for (const [key, value] of Object.entries(state.uiState.preferences)) { + await client.database.storeUIState(`pref_${key}`, value); + } + + // Persist identity updates back to LocalDatabase + for (const [address, identity] of Object.entries(state.identity.identitiesByAddress)) { + await client.database.upsertUserIdentity(address, identity); + } + } catch (error) { + console.warn('[StoreWiring] Persistence failed:', error); + } + }); + }); + + return () => { + unsubHealth?.(); + unsubMessages?.(); + unsubIdentity?.(); + unsubPersistence?.(); + }; + }, [client]); + + return null; +}; \ No newline at end of file diff --git a/packages/react/src/v1/store/opchanStore.ts b/packages/react/src/v1/store/opchanStore.ts new file mode 100644 index 0000000..5036734 --- /dev/null +++ b/packages/react/src/v1/store/opchanStore.ts @@ -0,0 +1,206 @@ +import React, { useSyncExternalStore } from 'react'; +import type { + Cell, + Post, + Comment, + Bookmark, + User, + EVerificationStatus, + DelegationFullStatus, +} from '@opchan/core'; +import type { UserIdentity } from '@opchan/core/dist/lib/services/UserIdentityService'; + +type Listener = () => void; + +export interface SessionSlice { + currentUser: User | null; + verificationStatus: EVerificationStatus; + delegation: DelegationFullStatus | null; +} + +export interface ContentSlice { + cells: Cell[]; + posts: Post[]; + comments: Comment[]; + bookmarks: Bookmark[]; + lastSync: number | null; + pendingIds: Set; + pendingVotes: Set; +} + +export interface IdentitySlice { + // Enhanced identity cache with full UserIdentity data + displayNameByAddress: Record; + identitiesByAddress: Record; + lastUpdatedByAddress: Record; +} + +export interface UIStateSlice { + // Centralized UI state to replace direct LocalDatabase access + wizardStates: Record; + preferences: Record; + temporaryStates: Record; +} + +export interface NetworkSlice { + isConnected: boolean; + statusMessage: string; + issues: string[]; +} + +export interface OpchanState { + session: SessionSlice; + content: ContentSlice; + identity: IdentitySlice; + uiState: UIStateSlice; + network: NetworkSlice; +} + +const defaultState: OpchanState = { + session: { + currentUser: null, + verificationStatus: 'wallet-unconnected' as EVerificationStatus, + delegation: null, + }, + content: { + cells: [], + posts: [], + comments: [], + bookmarks: [], + lastSync: null, + pendingIds: new Set(), + pendingVotes: new Set(), + }, + identity: { + displayNameByAddress: {}, + identitiesByAddress: {}, + lastUpdatedByAddress: {}, + }, + uiState: { + wizardStates: {}, + preferences: {}, + temporaryStates: {}, + }, + network: { + isConnected: false, + statusMessage: 'connecting…', + issues: [], + }, +}; + +class OpchanStoreImpl { + private state: OpchanState = defaultState; + private listeners: Set = new Set(); + private persistenceCallbacks: Set<(state: OpchanState) => Promise> = new Set(); + + subscribe(listener: Listener): () => void { + this.listeners.add(listener); + return () => this.listeners.delete(listener); + } + + getSnapshot(): OpchanState { + return this.state; + } + + private notify(): void { + for (const l of this.listeners) l(); + } + + setState(mutator: (prev: OpchanState) => OpchanState): void { + const next = mutator(this.state); + if (next !== this.state) { + this.state = next; + this.notify(); + this.triggerPersistence(next); + } + } + + // Enhanced methods for identity and UI state management + setIdentity(address: string, identity: UserIdentity): void { + this.setState(prev => ({ + ...prev, + identity: { + ...prev.identity, + displayNameByAddress: { + ...prev.identity.displayNameByAddress, + [address]: identity.displayName, + }, + identitiesByAddress: { + ...prev.identity.identitiesByAddress, + [address]: identity, + }, + lastUpdatedByAddress: { + ...prev.identity.lastUpdatedByAddress, + [address]: Date.now(), + }, + }, + })); + } + + setUIState(key: string, value: T, category: 'wizardStates' | 'preferences' | 'temporaryStates' = 'preferences'): void { + this.setState(prev => ({ + ...prev, + uiState: { + ...prev.uiState, + [category]: { + ...prev.uiState[category], + [key]: value, + }, + }, + })); + } + + getUIState(key: string, category: 'wizardStates' | 'preferences' | 'temporaryStates' = 'preferences'): T | undefined { + return this.state.uiState[category][key] as T; + } + + // Register persistence callbacks + onPersistence(callback: (state: OpchanState) => Promise): () => void { + this.persistenceCallbacks.add(callback); + return () => this.persistenceCallbacks.delete(callback); + } + + private async triggerPersistence(state: OpchanState): Promise { + // Run persistence callbacks asynchronously to avoid blocking UI + setTimeout(async () => { + for (const callback of this.persistenceCallbacks) { + try { + await callback(state); + } catch (error) { + console.warn('Store persistence callback failed:', error); + } + } + }, 0); + } +} + +export const opchanStore = new OpchanStoreImpl(); + +export function useOpchanStore(selector: (s: OpchanState) => T, isEqual?: (a: T, b: T) => boolean): T { + // Subscribe to the raw store snapshot to keep getSnapshot referentially stable + const state = useSyncExternalStore( + (cb) => opchanStore.subscribe(cb), + () => opchanStore.getSnapshot(), + () => opchanStore.getSnapshot(), + ); + + const compare = isEqual ?? ((a: T, b: T) => a === b); + const selected = React.useMemo(() => selector(state), [state, selector]); + + // Cache the last selected value using the provided equality to avoid churn + const cachedRef = React.useRef(selected); + if (!compare(cachedRef.current, selected)) { + cachedRef.current = selected; + } + return cachedRef.current; +} + +export function getOpchanState(): OpchanState { + return opchanStore.getSnapshot(); +} + +export function setOpchanState(mutator: (prev: OpchanState) => OpchanState): void { + opchanStore.setState(mutator); +} + + diff --git a/packages/react/tsconfig.build.json b/packages/react/tsconfig.build.json new file mode 100644 index 0000000..a7a7587 --- /dev/null +++ b/packages/react/tsconfig.build.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "dist", + "module": "esnext", + "jsx": "react-jsx", + "declaration": true, + "emitDeclarationOnly": false, + "declarationMap": false, + "skipLibCheck": true + }, + "include": ["src/**/*"] +} + + diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json new file mode 100644 index 0000000..72461b2 --- /dev/null +++ b/packages/react/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "jsx": "react-jsx", + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src/**/*"] +} + + diff --git a/packages/react/tsconfig.types.json b/packages/react/tsconfig.types.json new file mode 100644 index 0000000..b1d88b5 --- /dev/null +++ b/packages/react/tsconfig.types.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "module": "commonjs", + "jsx": "react-jsx", + "declaration": true, + "emitDeclarationOnly": true, + "skipLibCheck": true + }, + "include": ["src/**/*"] +} + + diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index a51f247..0000000 --- a/src/App.tsx +++ /dev/null @@ -1,67 +0,0 @@ -//TODO: research into having signatures somehow? -//TODO research: **each message sent should not be able to be spoofed** -/** - * Reference: - * https://www.notion.so/Logos-Forum-PoC-Waku-Powered-Opchan-1968f96fb65c8078b343c43429d66d0a#1968f96fb65c8025a929c2c9255a57c4 - * Also note that for UX purposes, **we should not ask a user to sign with their Bitcoin wallet for every action.** - * - * Instead, a key delegation system should be developed. - * - * - User sign an in-browser key with their wallet and broadcast it - * - Browser uses in-browser key to sign messages moving forward - */ - -import { Toaster } from '@/components/ui/toaster'; -import { Toaster as Sonner } from '@/components/ui/sonner'; -import { TooltipProvider } from '@/components/ui/tooltip'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; -import { AuthProvider } from '@/contexts/AuthContext'; -import { ForumProvider } from '@/contexts/ForumContext'; -import { ModerationProvider } from '@/contexts/ModerationContext'; -import CellPage from './pages/CellPage'; -import PostPage from './pages/PostPage'; -import NotFound from './pages/NotFound'; -import Dashboard from './pages/Dashboard'; -import Index from './pages/Index'; -import ProfilePage from './pages/ProfilePage'; -import BookmarksPage from './pages/BookmarksPage'; -import { appkitConfig } from './lib/wallet/config'; -import { WagmiProvider } from 'wagmi'; -import { config } from './lib/wallet/config'; -import { AppKitProvider } from '@reown/appkit/react'; - -// Create a client -const queryClient = new QueryClient(); - -const App = () => ( - - - - - - - - - - - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - - - - - - - -); - -export default App; diff --git a/src/components/ui/waku-health-indicator.tsx b/src/components/ui/waku-health-indicator.tsx deleted file mode 100644 index 3e93ae7..0000000 --- a/src/components/ui/waku-health-indicator.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { Wifi, WifiOff, AlertTriangle, CheckCircle } from 'lucide-react'; -import { useWakuHealthStatus } from '@/hooks/useWakuHealth'; -import { cn } from '@/lib/utils'; - -interface WakuHealthIndicatorProps { - className?: string; - showText?: boolean; - size?: 'sm' | 'md' | 'lg'; -} - -export function WakuHealthIndicator({ - className, - showText = true, - size = 'md', -}: WakuHealthIndicatorProps) { - const { connectionStatus, statusColor, statusMessage } = - useWakuHealthStatus(); - - const getIcon = () => { - switch (connectionStatus) { - case 'connected': - return ; - case 'connecting': - return ; - case 'disconnected': - return ; - case 'error': - return ; - default: - return ; - } - }; - - const getSizeClasses = () => { - switch (size) { - case 'sm': - return 'w-4 h-4'; - case 'lg': - return 'w-6 h-6'; - default: - return 'w-5 h-5'; - } - }; - - return ( -
-
{getIcon()}
- {showText && ( - - {statusMessage} - - )} -
- ); -} - -/** - * Simple dot indicator for Waku health status - * Useful for compact displays like headers or status bars - */ -export function WakuHealthDot({ className }: { className?: string }) { - const { statusColor } = useWakuHealthStatus(); - - return ( -
- ); -} diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx deleted file mode 100644 index bdeb7b8..0000000 --- a/src/contexts/AuthContext.tsx +++ /dev/null @@ -1,570 +0,0 @@ -import React, { createContext, useState, useEffect, useMemo } from 'react'; -import { useToast } from '@/components/ui/use-toast'; -import { OpchanMessage } from '@/types/forum'; -import { - User, - EVerificationStatus, - EDisplayPreference, -} from '@/types/identity'; -import { WalletManager } from '@/lib/wallet'; -import { - DelegationManager, - DelegationDuration, - DelegationFullStatus, -} from '@/lib/delegation'; -import { localDatabase } from '@/lib/database/LocalDatabase'; -import { useAppKitAccount, useDisconnect, modal } from '@reown/appkit/react'; -import { MessageService } from '@/lib/services/MessageService'; -import { UserIdentityService } from '@/lib/services/UserIdentityService'; -import { reconnect } from '@wagmi/core'; -import { config as wagmiConfig } from '@/lib/wallet/config'; - -interface AuthContextType { - currentUser: User | null; - isAuthenticating: boolean; - isAuthenticated: boolean; - verificationStatus: EVerificationStatus; - connectWallet: () => Promise; - disconnectWallet: () => void; - verifyOwnership: () => Promise; - delegateKey: (duration?: DelegationDuration) => Promise; - getDelegationStatus: () => Promise; - clearDelegation: () => Promise; - signMessage: (message: OpchanMessage) => Promise; - verifyMessage: (message: OpchanMessage) => Promise; -} - -const AuthContext = createContext(undefined); - -export { AuthContext }; - -export function AuthProvider({ children }: { children: React.ReactNode }) { - const [currentUser, setCurrentUser] = useState(null); - const [isAuthenticating, setIsAuthenticating] = useState(false); - const [verificationStatus, setVerificationStatus] = - useState(EVerificationStatus.WALLET_UNCONNECTED); - const { toast } = useToast(); - - // Use AppKit hooks for multi-chain support - const bitcoinAccount = useAppKitAccount({ namespace: 'bip122' }); - const ethereumAccount = useAppKitAccount({ namespace: 'eip155' }); - - // Determine which account is connected - const isBitcoinConnected = bitcoinAccount.isConnected; - const isEthereumConnected = ethereumAccount.isConnected; - const isConnected = isBitcoinConnected || isEthereumConnected; - - // Get the active account info - const activeAccount = isBitcoinConnected ? bitcoinAccount : ethereumAccount; - const address = activeAccount.address; - - // Create manager instances - const delegationManager = useMemo(() => new DelegationManager(), []); - const messageService = useMemo( - () => new MessageService(delegationManager), - [delegationManager] - ); - const userIdentityService = useMemo( - () => new UserIdentityService(messageService), - [messageService] - ); - - // Create wallet manager when we have all dependencies - useEffect(() => { - if (modal && (bitcoinAccount.isConnected || ethereumAccount.isConnected)) { - try { - WalletManager.create(modal, bitcoinAccount, ethereumAccount); - } catch (error) { - console.warn('Failed to create WalletManager:', error); - WalletManager.clear(); - } - } else { - WalletManager.clear(); - } - }, [bitcoinAccount, ethereumAccount]); - - // Try to auto-reconnect EVM wallets on app mount - useEffect(() => { - void reconnect(wagmiConfig) - }, []); - - // Attempt reconnect when network comes back online - useEffect(() => { - const handleOnline = () => { - void reconnect(wagmiConfig) - }; - window.addEventListener('online', handleOnline); - return () => window.removeEventListener('online', handleOnline); - }, []); - - // Attempt reconnect when tab becomes visible again - useEffect(() => { - const handleVisibility = () => { - if (document.visibilityState === 'visible') { - reconnect(wagmiConfig).catch(() => {}); - } - }; - document.addEventListener('visibilitychange', handleVisibility); - return () => document.removeEventListener('visibilitychange', handleVisibility); - }, []); - - // Helper functions for user persistence - const loadStoredUser = async (): Promise => { - try { - return await localDatabase.loadUser(); - } catch (e) { - console.error('Failed to load stored user data', e); - return null; - } - }; - - const saveUser = async (user: User): Promise => { - try { - await localDatabase.storeUser(user); - } catch (e) { - console.error('Failed to save user data', e); - } - }; - - // Helper function for ownership verification (via UserIdentityService) - const verifyUserOwnership = async (user: User): Promise => { - try { - // Force fresh resolution to ensure API call happens during verification - const identity = await userIdentityService.getUserIdentityFresh( - user.address - ); - if (!identity) { - return { - ...user, - ensDetails: undefined, - ordinalDetails: undefined, - verificationStatus: EVerificationStatus.WALLET_CONNECTED, - lastChecked: Date.now(), - }; - } - - return { - ...user, - ensDetails: identity.ensName - ? { ensName: identity.ensName } - : undefined, - ordinalDetails: identity.ordinalDetails, - verificationStatus: identity.verificationStatus, - lastChecked: Date.now(), - }; - } catch (error) { - console.error( - 'Error verifying ownership via UserIdentityService:', - error - ); - return { - ...user, - ensDetails: undefined, - ordinalDetails: undefined, - verificationStatus: EVerificationStatus.WALLET_CONNECTED, - lastChecked: Date.now(), - }; - } - }; - - // Helper function for key delegation - const createUserDelegation = async ( - user: User, - duration: DelegationDuration = '7days' - ): Promise => { - try { - if (!WalletManager.hasInstance()) { - throw new Error( - 'Wallet not connected. Please ensure your wallet is connected.' - ); - } - - const walletManager = WalletManager.getInstance(); - - // Verify wallet type matches - if (walletManager.getWalletType() !== user.walletType) { - throw new Error( - `Expected ${user.walletType} wallet, but ${walletManager.getWalletType()} is connected.` - ); - } - - // Use the simplified delegation method - return await delegationManager.delegate( - user.address, - user.walletType, - duration, - message => walletManager.signMessage(message) - ); - } catch (error) { - console.error( - `Error creating key delegation for ${user.walletType}:`, - error - ); - return false; - } - }; - - // Sync with AppKit wallet state - useEffect(() => { - if (isConnected && address) { - // Check if we have a stored user for this address - loadStoredUser().then(async storedUser => { - if (storedUser && storedUser.address === address) { - // Use stored user data - setCurrentUser(storedUser); - setVerificationStatus(getVerificationStatus(storedUser)); - } else { - // Create new user from AppKit wallet - const newUser: User = { - address, - walletType: isBitcoinConnected ? 'bitcoin' : 'ethereum', - verificationStatus: EVerificationStatus.WALLET_CONNECTED, // Connected wallets get basic verification by default - displayPreference: EDisplayPreference.WALLET_ADDRESS, - lastChecked: Date.now(), - }; - - // For Ethereum wallets, try to check ENS ownership immediately - if (isEthereumConnected) { - try { - const walletManager = WalletManager.getInstance(); - walletManager - .getWalletInfo() - .then(async walletInfo => { - if (walletInfo?.ensName) { - const updatedUser = { - ...newUser, - ensDetails: { ensName: walletInfo.ensName }, - verificationStatus: - EVerificationStatus.ENS_ORDINAL_VERIFIED, - }; - setCurrentUser(updatedUser); - setVerificationStatus( - EVerificationStatus.ENS_ORDINAL_VERIFIED - ); - await saveUser(updatedUser); - await localDatabase.upsertUserIdentity( - updatedUser.address, - { - ensName: walletInfo.ensName, - verificationStatus: - EVerificationStatus.ENS_ORDINAL_VERIFIED, - lastUpdated: Date.now(), - } - ); - } else { - setCurrentUser(newUser); - setVerificationStatus(EVerificationStatus.WALLET_CONNECTED); - await saveUser(newUser); - await localDatabase.upsertUserIdentity(newUser.address, { - verificationStatus: EVerificationStatus.WALLET_CONNECTED, - lastUpdated: Date.now(), - }); - } - }) - .catch(async () => { - // Fallback to basic verification if ENS check fails - setCurrentUser(newUser); - setVerificationStatus(EVerificationStatus.WALLET_CONNECTED); - await saveUser(newUser); - await localDatabase.upsertUserIdentity(newUser.address, { - verificationStatus: EVerificationStatus.WALLET_CONNECTED, - lastUpdated: Date.now(), - }); - }); - } catch { - // WalletManager not ready, fallback to basic verification - setCurrentUser(newUser); - setVerificationStatus(EVerificationStatus.WALLET_CONNECTED); - await saveUser(newUser); - await localDatabase.upsertUserIdentity(newUser.address, { - verificationStatus: EVerificationStatus.WALLET_CONNECTED, - lastUpdated: Date.now(), - }); - } - } else { - setCurrentUser(newUser); - setVerificationStatus(EVerificationStatus.WALLET_CONNECTED); - await saveUser(newUser); - await localDatabase.upsertUserIdentity(newUser.address, { - verificationStatus: EVerificationStatus.WALLET_CONNECTED, - lastUpdated: Date.now(), - }); - } - - const chainName = isBitcoinConnected ? 'Bitcoin' : 'Ethereum'; - // Note: We can't use useUserDisplay hook here since this is not a React component - // This is just for toast messages, so simple truncation is acceptable - const displayName = `${address.slice(0, 6)}...${address.slice(-4)}`; - - toast({ - title: 'Wallet Connected', - description: `Connected to ${chainName} with ${displayName}`, - }); - - const verificationType = isBitcoinConnected - ? 'Ordinal ownership' - : 'ENS ownership'; - toast({ - title: 'Action Required', - description: `You can participate in the forum now! Verify your ${verificationType} for premium features and delegate a signing key for better UX.`, - }); - } - }); - } else { - // Wallet disconnected - setCurrentUser(null); - setVerificationStatus(EVerificationStatus.WALLET_UNCONNECTED); - } - }, [isConnected, address, isBitcoinConnected, isEthereumConnected, toast]); - - const { disconnect } = useDisconnect(); - - const connectWallet = async (): Promise => { - try { - if (modal) { - await modal.open(); - return true; - } - return false; - } catch (error) { - console.error('Error connecting wallet:', error); - return false; - } - }; - - const disconnectWallet = (): void => { - disconnect(); - }; - - const getVerificationStatus = (user: User): EVerificationStatus => { - if (user.walletType === 'bitcoin') { - return user.ordinalDetails - ? EVerificationStatus.ENS_ORDINAL_VERIFIED - : EVerificationStatus.WALLET_CONNECTED; - } else if (user.walletType === 'ethereum') { - return user.ensDetails - ? EVerificationStatus.ENS_ORDINAL_VERIFIED - : EVerificationStatus.WALLET_CONNECTED; - } - return EVerificationStatus.WALLET_UNCONNECTED; - }; - - const verifyOwnership = async (): Promise => { - if (!currentUser || !currentUser.address) { - toast({ - title: 'Not Connected', - description: 'Please connect your wallet first.', - variant: 'destructive', - }); - return false; - } - - setIsAuthenticating(true); - setVerificationStatus(EVerificationStatus.WALLET_CONNECTED); // Temporary state during verification - - try { - const verificationType = - currentUser.walletType === 'bitcoin' ? 'Ordinal' : 'ENS'; - toast({ - title: `Verifying ${verificationType}`, - description: `Checking your wallet for ${verificationType} ownership...`, - }); - - const updatedUser = await verifyUserOwnership(currentUser); - setCurrentUser(updatedUser); - await saveUser(updatedUser); - - // Persist centralized identity (ENS/verification) for display everywhere - await localDatabase.upsertUserIdentity(updatedUser.address, { - ensName: updatedUser.ensDetails?.ensName, - ordinalDetails: updatedUser.ordinalDetails, - verificationStatus: updatedUser.verificationStatus, - lastUpdated: Date.now(), - }); - - // Update verification status - setVerificationStatus(getVerificationStatus(updatedUser)); - - if (updatedUser.walletType === 'bitcoin' && updatedUser.ordinalDetails) { - toast({ - title: 'Ordinal Verified', - description: - 'You now have premium access with higher relevance bonuses. We recommend delegating a key for better UX.', - }); - } else if ( - updatedUser.walletType === 'ethereum' && - updatedUser.ensDetails - ) { - toast({ - title: 'ENS Verified', - description: - 'You now have premium access with higher relevance bonuses. We recommend delegating a key for better UX.', - }); - } else { - const verificationType = - updatedUser.walletType === 'bitcoin' - ? 'Ordinal Operators' - : 'ENS domain'; - toast({ - title: 'Basic Access Granted', - description: `No ${verificationType} found, but you can still participate in the forum with your connected wallet.`, - variant: 'default', - }); - } - - return Boolean( - (updatedUser.walletType === 'bitcoin' && updatedUser.ordinalDetails) || - (updatedUser.walletType === 'ethereum' && updatedUser.ensDetails) - ); - } catch (error) { - console.error('Error verifying ownership:', error); - setVerificationStatus(EVerificationStatus.WALLET_UNCONNECTED); - - let errorMessage = 'Failed to verify ownership. Please try again.'; - if (error instanceof Error) { - errorMessage = error.message; - } - - toast({ - title: 'Verification Error', - description: errorMessage, - variant: 'destructive', - }); - - return false; - } finally { - setIsAuthenticating(false); - } - }; - - const delegateKey = async ( - duration: DelegationDuration = '7days' - ): Promise => { - if (!currentUser) { - toast({ - title: 'No User Found', - description: 'Please connect your wallet first.', - variant: 'destructive', - }); - return false; - } - - setIsAuthenticating(true); - - try { - const durationText = duration === '7days' ? '1 week' : '30 days'; - toast({ - title: 'Starting Key Delegation', - description: `This will let you post, comment, and vote without approving each action for ${durationText}.`, - }); - - const success = await createUserDelegation(currentUser, duration); - if (!success) { - throw new Error('Failed to create key delegation'); - } - - // Update user with delegation info - const delegationStatus = await delegationManager.getStatus( - currentUser.address, - currentUser.walletType - ); - - const updatedUser = { - ...currentUser, - browserPubKey: delegationStatus.publicKey || undefined, - delegationSignature: delegationStatus.isValid ? 'valid' : undefined, - delegationExpiry: delegationStatus.timeRemaining - ? Date.now() + delegationStatus.timeRemaining - : undefined, - }; - - setCurrentUser(updatedUser); - await saveUser(updatedUser); - - // Format date for user-friendly display - const expiryDate = new Date(updatedUser.delegationExpiry!); - const formattedExpiry = expiryDate.toLocaleString(); - - toast({ - title: 'Key Delegation Successful', - description: `You can now interact with the forum without additional wallet approvals until ${formattedExpiry}.`, - }); - - return true; - } catch (error) { - console.error('Error delegating key:', error); - - let errorMessage = 'Failed to delegate key. Please try again.'; - if (error instanceof Error) { - errorMessage = error.message; - } - - toast({ - title: 'Delegation Error', - description: errorMessage, - variant: 'destructive', - }); - - return false; - } finally { - setIsAuthenticating(false); - } - }; - - const getDelegationStatus = async (): Promise => { - return await delegationManager.getStatus( - currentUser?.address, - currentUser?.walletType - ); - }; - - const clearDelegation = async (): Promise => { - await delegationManager.clear(); - - // Update the current user to remove delegation info - if (currentUser) { - const updatedUser = { - ...currentUser, - delegationExpiry: undefined, - browserPubKey: undefined, - delegationSignature: undefined, - }; - setCurrentUser(updatedUser); - await saveUser(updatedUser); - } - - toast({ - title: 'Delegation Cleared', - description: - "Your delegated signing key has been removed. You'll need to delegate a new key to continue posting and voting.", - }); - }; - - const messageSigning = { - signMessage: async ( - message: OpchanMessage - ): Promise => { - return await delegationManager.signMessage(message); - }, - verifyMessage: async (message: OpchanMessage): Promise => { - return await delegationManager.verify(message); - }, - }; - - const value: AuthContextType = { - currentUser, - isAuthenticating, - isAuthenticated: Boolean(currentUser && isConnected), - verificationStatus, - connectWallet, - disconnectWallet, - verifyOwnership, - delegateKey, - getDelegationStatus, - clearDelegation, - signMessage: messageSigning.signMessage, - verifyMessage: messageSigning.verifyMessage, - }; - - return {children}; -} diff --git a/src/contexts/ForumContext.tsx b/src/contexts/ForumContext.tsx deleted file mode 100644 index e63e743..0000000 --- a/src/contexts/ForumContext.tsx +++ /dev/null @@ -1,777 +0,0 @@ -import React, { - createContext, - useState, - useEffect, - useCallback, - useMemo, - useRef, -} from 'react'; -import { Cell, Post, Comment } from '@/types/forum'; -import { - User, - EVerificationStatus, - EDisplayPreference, -} from '@/types/identity'; -import { useToast } from '@/components/ui/use-toast'; - -import { ForumActions } from '@/lib/forum/ForumActions'; -import { monitorNetworkHealth, initializeNetwork } from '@/lib/waku/network'; -import messageManager from '@/lib/waku'; -import { getDataFromCache } from '@/lib/forum/transformers'; -import { RelevanceCalculator } from '@/lib/forum/RelevanceCalculator'; -import { UserVerificationStatus } from '@/types/forum'; -import { DelegationManager } from '@/lib/delegation'; -import { UserIdentityService } from '@/lib/services/UserIdentityService'; -import { MessageService } from '@/lib/services/MessageService'; -import { useAuth } from '@/contexts/useAuth'; -import { localDatabase } from '@/lib/database/LocalDatabase'; - -interface ForumContextType { - cells: Cell[]; - posts: Post[]; - comments: Comment[]; - // User verification status for display - userVerificationStatus: UserVerificationStatus; - // User identity service for profile management - userIdentityService: UserIdentityService | null; - // Granular loading states - isInitialLoading: boolean; - // Sync state - lastSync: number | null; - isSyncing: boolean; - isPostingCell: boolean; - isPostingPost: boolean; - isPostingComment: boolean; - isVoting: boolean; - isRefreshing: boolean; - // Network status - isNetworkConnected: boolean; - error: string | null; - getCellById: (id: string) => Cell | undefined; - getPostsByCell: (cellId: string) => Post[]; - getCommentsByPost: (postId: string) => Comment[]; - createPost: ( - cellId: string, - title: string, - content: string - ) => Promise; - createComment: (postId: string, content: string) => Promise; - votePost: (postId: string, isUpvote: boolean) => Promise; - voteComment: (commentId: string, isUpvote: boolean) => Promise; - createCell: ( - name: string, - description: string, - icon?: string - ) => Promise; - refreshData: () => Promise; - moderatePost: ( - cellId: string, - postId: string, - reason: string | undefined, - cellOwner: string - ) => Promise; - unmoderatePost: ( - cellId: string, - postId: string, - reason: string | undefined, - cellOwner: string - ) => Promise; - moderateComment: ( - cellId: string, - commentId: string, - reason: string | undefined, - cellOwner: string - ) => Promise; - unmoderateComment: ( - cellId: string, - commentId: string, - reason: string | undefined, - cellOwner: string - ) => Promise; - moderateUser: ( - cellId: string, - userAddress: string, - reason: string | undefined, - cellOwner: string - ) => Promise; - unmoderateUser: ( - cellId: string, - userAddress: string, - reason: string | undefined, - cellOwner: string - ) => Promise; -} - -const ForumContext = createContext(undefined); - -export { ForumContext }; - -export function ForumProvider({ children }: { children: React.ReactNode }) { - const [cells, setCells] = useState([]); - const [posts, setPosts] = useState([]); - const [comments, setComments] = useState([]); - const [isInitialLoading, setIsInitialLoading] = useState(true); - const [{ lastSync, isSyncing }, setSyncState] = useState({ - lastSync: null as number | null, - isSyncing: false, - }); - const [isPostingCell, setIsPostingCell] = useState(false); - const [isPostingPost, setIsPostingPost] = useState(false); - const [isPostingComment, setIsPostingComment] = useState(false); - const [isVoting, setIsVoting] = useState(false); - const [isRefreshing, setIsRefreshing] = useState(false); - const [isNetworkConnected, setIsNetworkConnected] = useState(false); - const [error, setError] = useState(null); - const [userVerificationStatus, setUserVerificationStatus] = - useState({}); - - const { toast } = useToast(); - const { currentUser, isAuthenticated } = useAuth(); - - const delegationManager = useMemo(() => new DelegationManager(), []); - const messageService = useMemo( - () => new MessageService(delegationManager), - [delegationManager] - ); - const userIdentityService = useMemo( - () => new UserIdentityService(messageService), - [messageService] - ); - const forumActions = useMemo( - () => new ForumActions(delegationManager), - [delegationManager] - ); - - // Transform message cache data to the expected types - const updateStateFromCache = useCallback(async () => { - // Build user verification status for relevance calculation - const relevanceCalculator = new RelevanceCalculator(); - const allUsers: User[] = []; - - // Collect all unique users from posts, comments, and votes - const userAddresses = new Set(); - - // Add users from posts - Object.values(messageManager.messageCache.posts).forEach(post => { - userAddresses.add(post.author); - }); - - // Add users from comments - Object.values(messageManager.messageCache.comments).forEach(comment => { - userAddresses.add(comment.author); - }); - - // Add users from votes - Object.values(messageManager.messageCache.votes).forEach(vote => { - userAddresses.add(vote.author); - }); - - // Create user objects for verification status using UserIdentityService - const userIdentityPromises = Array.from(userAddresses).map( - async address => { - // Check if this address matches the current user's address - if (currentUser && currentUser.address === address) { - // Use the current user's actual verification status - return { - address, - walletType: currentUser.walletType, - verificationStatus: currentUser.verificationStatus, - displayPreference: currentUser.displayPreference, - ensDetails: currentUser.ensDetails, - ordinalDetails: currentUser.ordinalDetails, - lastChecked: currentUser.lastChecked, - }; - } else { - // Use UserIdentityService to get identity information - const identity = await userIdentityService.getUserIdentity(address); - if (identity) { - return { - address, - walletType: address.startsWith('0x') - ? ('ethereum' as const) - : ('bitcoin' as const), - verificationStatus: identity.verificationStatus, - displayPreference: identity.displayPreference, - ensDetails: identity.ensName - ? { ensName: identity.ensName } - : undefined, - ordinalDetails: identity.ordinalDetails, - lastChecked: identity.lastUpdated, - }; - } else { - // Fallback to generic user object - return { - address, - walletType: address.startsWith('0x') - ? ('ethereum' as const) - : ('bitcoin' as const), - verificationStatus: EVerificationStatus.WALLET_UNCONNECTED, - displayPreference: EDisplayPreference.WALLET_ADDRESS, - }; - } - } - } - ); - - const resolvedUsers = await Promise.all(userIdentityPromises); - allUsers.push(...resolvedUsers); - - const initialStatus = - relevanceCalculator.buildUserVerificationStatus(allUsers); - - // Transform data with relevance calculation - const { cells, posts, comments } = await getDataFromCache( - undefined, - initialStatus - ); - - setCells(cells); - setPosts(posts); - setComments(comments); - setUserVerificationStatus(initialStatus); - // Sync state from LocalDatabase - setSyncState(localDatabase.getSyncState()); - }, [currentUser, userIdentityService]); - - const handleRefreshData = async () => { - setIsRefreshing(true); - try { - // SDS handles message syncing automatically, just update UI - await updateStateFromCache(); - const { lastSync, isSyncing } = localDatabase.getSyncState(); - toast({ - title: 'Data Refreshed', - description: lastSync - ? `Your view has been updated. Last sync: ${new Date( - lastSync - ).toLocaleTimeString()}${isSyncing ? ' (syncing...)' : ''}` - : 'Your view has been updated.', - }); - } catch (error) { - console.error('Error refreshing data:', error); - toast({ - title: 'Refresh Failed', - description: 'Could not update the view. Please try again.', - variant: 'destructive', - }); - } finally { - setIsRefreshing(false); - } - }; - - // Monitor network connection status - useEffect(() => { - const { unsubscribe } = monitorNetworkHealth(setIsNetworkConnected, toast); - return unsubscribe; - }, [toast]); - - const hasInitializedRef = useRef(false); - - useEffect(() => { - if (hasInitializedRef.current) return; - hasInitializedRef.current = true; - const loadData = async () => { - setIsInitialLoading(true); - // Open Local DB and seed Waku cache on warm start before network init - try { - await localDatabase.open(); - // Seed messageManager's in-memory cache from LocalDatabase for instant UI - const seeded = localDatabase.cache; - Object.assign(messageManager.messageCache.cells, seeded.cells); - Object.assign(messageManager.messageCache.posts, seeded.posts); - Object.assign(messageManager.messageCache.comments, seeded.comments); - Object.assign(messageManager.messageCache.votes, seeded.votes); - Object.assign( - messageManager.messageCache.moderations, - seeded.moderations - ); - Object.assign( - messageManager.messageCache.userIdentities, - seeded.userIdentities - ); - - // Determine if we have any cached content - const hasSeedData = - Object.keys(seeded.cells).length > 0 || - Object.keys(seeded.posts).length > 0 || - Object.keys(seeded.comments).length > 0 || - Object.keys(seeded.votes).length > 0; - - // Render from local cache immediately (warm start) or empty (cold) - await updateStateFromCache(); - - // Initialize network and let incoming messages update LocalDatabase/Cache - await initializeNetwork(toast, setError); - - if (hasSeedData) { - setIsInitialLoading(false); - } else { - // Wait for Waku network to be healthy instead of first message - const unsubscribeHealth = messageManager.onHealthChange(isReady => { - if (isReady) { - setIsInitialLoading(false); - unsubscribeHealth(); - } - }); - } - } catch (e) { - console.warn('LocalDatabase warm-start failed, continuing cold:', e); - // Initialize network even if local DB failed, keep loader until Waku is healthy - await initializeNetwork(toast, setError); - - const unsubscribeHealth = messageManager.onHealthChange(isReady => { - if (isReady) { - setIsInitialLoading(false); - unsubscribeHealth(); - } - }); - } - }; - - loadData(); - - // Set up periodic queries - // Setup periodic queries would go here - // const { cleanup } = setupPeriodicQueries(updateStateFromCache); - - return () => {}; // Return empty cleanup function - }, [toast, updateStateFromCache]); - - // Subscribe to incoming messages to update UI in real-time - useEffect(() => { - const unsubscribe = messageManager.onMessageReceived(() => { - localDatabase.setSyncing(true); - updateStateFromCache().finally(() => localDatabase.setSyncing(false)); - }); - return unsubscribe; - }, [updateStateFromCache]); - - // Simple reactive updates: check for new data periodically when connected - useEffect(() => { - if (!isNetworkConnected) return; - - const interval = setInterval(() => { - // Only update if we're connected and ready - if (messageManager.isReady) { - localDatabase.setSyncing(true); - updateStateFromCache().finally(() => localDatabase.setSyncing(false)); - } - }, 15000); // 15 seconds - much less frequent than before - - return () => clearInterval(interval); - }, [isNetworkConnected, updateStateFromCache]); - - const getCellById = (id: string): Cell | undefined => { - return cells.find(cell => cell.id === id); - }; - - const getPostsByCell = (cellId: string): Post[] => { - return posts - .filter(post => post.cellId === cellId) - .sort((a, b) => b.timestamp - a.timestamp); - }; - - const getCommentsByPost = (postId: string): Comment[] => { - return comments - .filter(comment => comment.postId === postId) - .sort((a, b) => a.timestamp - b.timestamp); - }; - - const handleCreatePost = async ( - cellId: string, - title: string, - content: string - ): Promise => { - setIsPostingPost(true); - toast({ - title: 'Creating post', - description: 'Sending your post to the network...', - }); - - const result = await forumActions.createPost( - { cellId, title, content, currentUser, isAuthenticated }, - updateStateFromCache - ); - - setIsPostingPost(false); - - if (result.success) { - toast({ - title: 'Post Created', - description: 'Your post has been published successfully.', - }); - return result.data || null; - } else { - toast({ - title: 'Post Failed', - description: result.error || 'Failed to create post. Please try again.', - variant: 'destructive', - }); - return null; - } - }; - - const handleCreateComment = async ( - postId: string, - content: string - ): Promise => { - setIsPostingComment(true); - toast({ - title: 'Posting comment', - description: 'Sending your comment to the network...', - }); - - const result = await forumActions.createComment( - { postId, content, currentUser, isAuthenticated }, - updateStateFromCache - ); - - setIsPostingComment(false); - - if (result.success) { - toast({ - title: 'Comment Added', - description: 'Your comment has been published.', - }); - return result.data || null; - } else { - toast({ - title: 'Comment Failed', - description: result.error || 'Failed to add comment. Please try again.', - variant: 'destructive', - }); - return null; - } - }; - - const handleVotePost = async ( - postId: string, - isUpvote: boolean - ): Promise => { - setIsVoting(true); - const voteType = isUpvote ? 'upvote' : 'downvote'; - toast({ - title: `Sending ${voteType}`, - description: 'Recording your vote on the network...', - }); - - const result = await forumActions.vote( - { targetId: postId, isUpvote, currentUser, isAuthenticated }, - updateStateFromCache - ); - - setIsVoting(false); - - if (result.success) { - toast({ - title: 'Vote Recorded', - description: `Your ${voteType} has been registered.`, - }); - return result.data || false; - } else { - toast({ - title: 'Vote Failed', - description: - result.error || 'Failed to register your vote. Please try again.', - variant: 'destructive', - }); - return false; - } - }; - - const handleVoteComment = async ( - commentId: string, - isUpvote: boolean - ): Promise => { - setIsVoting(true); - const voteType = isUpvote ? 'upvote' : 'downvote'; - toast({ - title: `Sending ${voteType}`, - description: 'Recording your vote on the network...', - }); - - const result = await forumActions.vote( - { targetId: commentId, isUpvote, currentUser, isAuthenticated }, - updateStateFromCache - ); - - setIsVoting(false); - - if (result.success) { - toast({ - title: 'Vote Recorded', - description: `Your ${voteType} has been registered.`, - }); - return result.data || false; - } else { - toast({ - title: 'Vote Failed', - description: - result.error || 'Failed to register your vote. Please try again.', - variant: 'destructive', - }); - return false; - } - }; - - const handleCreateCell = async ( - name: string, - description: string, - icon?: string - ): Promise => { - setIsPostingCell(true); - toast({ - title: 'Creating cell', - description: 'Sending your cell to the network...', - }); - - const result = await forumActions.createCell( - { name, description, icon, currentUser, isAuthenticated }, - updateStateFromCache - ); - - setIsPostingCell(false); - - if (result.success) { - toast({ - title: 'Cell Created', - description: 'Your cell has been published.', - }); - return result.data || null; - } else { - toast({ - title: 'Cell Failed', - description: result.error || 'Failed to create cell. Please try again.', - variant: 'destructive', - }); - return null; - } - }; - - const handleModeratePost = async ( - cellId: string, - postId: string, - reason: string | undefined, - cellOwner: string - ) => { - toast({ - title: 'Moderating Post', - description: 'Sending moderation message to the network...', - }); - - const result = await forumActions.moderatePost( - { cellId, postId, reason, currentUser, isAuthenticated, cellOwner }, - updateStateFromCache - ); - - if (result.success) { - toast({ - title: 'Post Moderated', - description: 'The post has been marked as moderated.', - }); - return result.data || false; - } else { - toast({ - title: 'Moderation Failed', - description: - result.error || 'Failed to moderate post. Please try again.', - variant: 'destructive', - }); - return false; - } - }; - - const handleUnmoderatePost = async ( - cellId: string, - postId: string, - reason: string | undefined, - cellOwner: string - ) => { - toast({ - title: 'Unmoderating Post', - description: 'Sending unmoderation message to the network...', - }); - - const result = await forumActions.unmoderatePost( - { cellId, postId, reason, currentUser, isAuthenticated, cellOwner }, - updateStateFromCache - ); - - if (result.success) { - toast({ - title: 'Post Unmoderated', - description: 'The post is now visible again.', - }); - return result.data || false; - } else { - toast({ - title: 'Unmoderation Failed', - description: - result.error || 'Failed to unmoderate post. Please try again.', - variant: 'destructive', - }); - return false; - } - }; - - const handleModerateComment = async ( - cellId: string, - commentId: string, - reason: string | undefined, - cellOwner: string - ) => { - toast({ - title: 'Moderating Comment', - description: 'Sending moderation message to the network...', - }); - - const result = await forumActions.moderateComment( - { cellId, commentId, reason, currentUser, isAuthenticated, cellOwner }, - updateStateFromCache - ); - - if (result.success) { - toast({ - title: 'Comment Moderated', - description: 'The comment has been marked as moderated.', - }); - return result.data || false; - } else { - toast({ - title: 'Moderation Failed', - description: - result.error || 'Failed to moderate comment. Please try again.', - variant: 'destructive', - }); - return false; - } - }; - - const handleUnmoderateComment = async ( - cellId: string, - commentId: string, - reason: string | undefined, - cellOwner: string - ) => { - toast({ - title: 'Unmoderating Comment', - description: 'Sending unmoderation message to the network...', - }); - - const result = await forumActions.unmoderateComment( - { cellId, commentId, reason, currentUser, isAuthenticated, cellOwner }, - updateStateFromCache - ); - - if (result.success) { - toast({ - title: 'Comment Unmoderated', - description: 'The comment is now visible again.', - }); - return result.data || false; - } else { - toast({ - title: 'Unmoderation Failed', - description: - result.error || 'Failed to unmoderate comment. Please try again.', - variant: 'destructive', - }); - return false; - } - }; - - const handleModerateUser = async ( - cellId: string, - userAddress: string, - reason: string | undefined, - cellOwner: string - ) => { - const result = await forumActions.moderateUser( - { cellId, userAddress, reason, currentUser, isAuthenticated, cellOwner }, - updateStateFromCache - ); - - if (result.success) { - toast({ - title: 'User Moderated', - description: `User ${userAddress} has been moderated in this cell.`, - }); - return result.data || false; - } else { - toast({ - title: 'Moderation Failed', - description: - result.error || 'Failed to moderate user. Please try again.', - variant: 'destructive', - }); - return false; - } - }; - - const handleUnmoderateUser = async ( - cellId: string, - userAddress: string, - reason: string | undefined, - cellOwner: string - ) => { - const result = await forumActions.unmoderateUser( - { cellId, userAddress, reason, currentUser, isAuthenticated, cellOwner }, - updateStateFromCache - ); - - if (result.success) { - toast({ - title: 'User Unmoderated', - description: `User ${userAddress} has been unmoderated in this cell.`, - }); - return result.data || false; - } else { - toast({ - title: 'Unmoderation Failed', - description: - result.error || 'Failed to unmoderate user. Please try again.', - variant: 'destructive', - }); - return false; - } - }; - - return ( - - {children} - - ); -} diff --git a/src/contexts/ModerationContext.tsx b/src/contexts/ModerationContext.tsx deleted file mode 100644 index 09f7132..0000000 --- a/src/contexts/ModerationContext.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React, { createContext, useContext, useState, useEffect } from 'react'; -import { localDatabase } from '@/lib/database/LocalDatabase'; - -interface ModerationContextType { - showModerated: boolean; - setShowModerated: (show: boolean) => void; - toggleShowModerated: () => void; -} - -const ModerationContext = createContext( - undefined -); - -export function ModerationProvider({ - children, -}: { - children: React.ReactNode; -}) { - const [showModerated, setShowModerated] = useState(false); - const [isInitialized, setIsInitialized] = useState(false); - - // Load initial state from IndexedDB - useEffect(() => { - const loadModerationPreference = async () => { - try { - const saved = await localDatabase.loadUIState('show-moderated'); - setShowModerated(saved === true); - } catch (error) { - console.warn( - 'Failed to load moderation preference from IndexedDB:', - error - ); - setShowModerated(false); - } finally { - setIsInitialized(true); - } - }; - - loadModerationPreference(); - }, []); - - // Save to IndexedDB whenever the value changes (but only after initialization) - useEffect(() => { - if (!isInitialized) return; - - const saveModerationPreference = async () => { - try { - await localDatabase.storeUIState('show-moderated', showModerated); - } catch (error) { - console.warn( - 'Failed to save moderation preference to IndexedDB:', - error - ); - } - }; - - saveModerationPreference(); - }, [showModerated, isInitialized]); - - const toggleShowModerated = () => { - setShowModerated(prev => !prev); - }; - - return ( - - {children} - - ); -} - -export function useModeration() { - const context = useContext(ModerationContext); - if (context === undefined) { - throw new Error('useModeration must be used within a ModerationProvider'); - } - return context; -} diff --git a/src/contexts/useAuth.ts b/src/contexts/useAuth.ts deleted file mode 100644 index 2966886..0000000 --- a/src/contexts/useAuth.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useContext } from 'react'; -import { AuthContext } from './AuthContext'; - -export const useAuth = () => { - const context = useContext(AuthContext); - if (context === undefined) { - throw new Error('useAuth must be used within an AuthProvider'); - } - return context; -}; diff --git a/src/contexts/useForum.ts b/src/contexts/useForum.ts deleted file mode 100644 index 7870a1d..0000000 --- a/src/contexts/useForum.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useContext } from 'react'; -import { ForumContext } from './ForumContext'; - -export const useForum = () => { - const context = useContext(ForumContext); - if (context === undefined) { - throw new Error('useForum must be used within a ForumProvider'); - } - return context; -}; diff --git a/src/hooks/actions/useAuthActions.ts b/src/hooks/actions/useAuthActions.ts deleted file mode 100644 index 97e3dba..0000000 --- a/src/hooks/actions/useAuthActions.ts +++ /dev/null @@ -1,323 +0,0 @@ -import { useCallback, useState } from 'react'; -import { useAuth } from '@/hooks/core/useAuth'; -import { DelegationDuration } from '@/lib/delegation'; -import { useToast } from '@/components/ui/use-toast'; -import { useAuth as useAuthContext } from '@/contexts/useAuth'; -import { EVerificationStatus } from '@/types/identity'; - -export interface AuthActionStates { - isConnecting: boolean; - isVerifying: boolean; - isDelegating: boolean; - isDisconnecting: boolean; -} - -export interface AuthActions extends AuthActionStates { - // Connection actions - connectWallet: () => Promise; - disconnectWallet: () => Promise; - - // Verification actions - verifyWallet: () => Promise; - - // Delegation actions - delegateKey: (duration: DelegationDuration) => Promise; - clearDelegation: () => Promise; - renewDelegation: (duration: DelegationDuration) => Promise; - - // Utility actions - checkVerificationStatus: () => Promise; -} - -/** - * Hook for authentication and verification actions - */ -export function useAuthActions(): AuthActions { - const { isAuthenticated, isAuthenticating, verificationStatus } = useAuth(); - - const { - verifyOwnership, - delegateKey: delegateKeyFromContext, - getDelegationStatus, - clearDelegation: clearDelegationFromContext, - } = useAuthContext(); - const { toast } = useToast(); - - const [isConnecting, setIsConnecting] = useState(false); - const [isVerifying, setIsVerifying] = useState(false); - const [isDelegating, setIsDelegating] = useState(false); - const [isDisconnecting, setIsDisconnecting] = useState(false); - - // Connect wallet - const connectWallet = useCallback(async (): Promise => { - if (isAuthenticated) { - toast({ - title: 'Already Connected', - description: 'Your wallet is already connected.', - }); - return true; - } - - setIsConnecting(true); - - try { - // This would trigger the wallet connection flow - // The actual implementation would depend on the wallet system - // For now, we'll assume it's handled by the auth context - - toast({ - title: 'Connecting...', - description: 'Please approve the connection in your wallet.', - }); - - // Wait for authentication to complete - // This is a simplified implementation - await new Promise(resolve => setTimeout(resolve, 2000)); - - if (isAuthenticated) { - toast({ - title: 'Wallet Connected', - description: 'Your wallet has been connected successfully.', - }); - return true; - } else { - toast({ - title: 'Connection Failed', - description: 'Failed to connect wallet. Please try again.', - variant: 'destructive', - }); - return false; - } - } catch (error) { - console.error('Failed to connect wallet:', error); - toast({ - title: 'Connection Error', - description: 'An error occurred while connecting your wallet.', - variant: 'destructive', - }); - return false; - } finally { - setIsConnecting(false); - } - }, [isAuthenticated, toast]); - - // Disconnect wallet - const disconnectWallet = useCallback(async (): Promise => { - if (!isAuthenticated) { - toast({ - title: 'Not Connected', - description: 'No wallet is currently connected.', - }); - return true; - } - - setIsDisconnecting(true); - - try { - // This would trigger the wallet disconnection - // The actual implementation would depend on the wallet system - - toast({ - title: 'Wallet Disconnected', - description: 'Your wallet has been disconnected.', - }); - return true; - } catch (error) { - console.error('Failed to disconnect wallet:', error); - toast({ - title: 'Disconnection Error', - description: 'An error occurred while disconnecting your wallet.', - variant: 'destructive', - }); - return false; - } finally { - setIsDisconnecting(false); - } - }, [isAuthenticated, toast]); - - // Verify wallet - const verifyWallet = useCallback(async (): Promise => { - if (!isAuthenticated) { - toast({ - title: 'Wallet Not Connected', - description: 'Please connect your wallet first.', - variant: 'destructive', - }); - return false; - } - - if (verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED) { - toast({ - title: 'Already Verified', - description: 'Your wallet is already verified.', - }); - return true; - } - - setIsVerifying(true); - - try { - // Call the real verification function from AuthContext - const success = await verifyOwnership(); - - if (success) { - toast({ - title: 'Verification Complete', - description: 'Your wallet has been verified successfully.', - }); - } else { - toast({ - title: 'Verification Failed', - description: 'Failed to verify wallet ownership. Please try again.', - variant: 'destructive', - }); - } - - return success; - } catch (error) { - console.error('Failed to verify wallet:', error); - toast({ - title: 'Verification Failed', - description: 'Failed to verify wallet. Please try again.', - variant: 'destructive', - }); - return false; - } finally { - setIsVerifying(false); - } - }, [isAuthenticated, verificationStatus, verifyOwnership, toast]); - - // Delegate key - const delegateKey = useCallback( - async (duration: DelegationDuration): Promise => { - if (!isAuthenticated) { - toast({ - title: 'Wallet Not Connected', - description: 'Please connect your wallet first.', - variant: 'destructive', - }); - return false; - } - - if (verificationStatus === EVerificationStatus.WALLET_UNCONNECTED) { - toast({ - title: 'Verification Required', - description: 'Please verify your wallet before delegating keys.', - variant: 'destructive', - }); - return false; - } - - setIsDelegating(true); - - try { - // Call the real delegation function from AuthContext - const success = await delegateKeyFromContext(duration); - - if (success) { - const durationLabel = duration === '7days' ? '1 week' : '30 days'; - toast({ - title: 'Key Delegated', - description: `Your signing key has been delegated for ${durationLabel}.`, - }); - } else { - toast({ - title: 'Delegation Failed', - description: 'Failed to delegate signing key. Please try again.', - variant: 'destructive', - }); - } - - return success; - } catch (error) { - console.error('Failed to delegate key:', error); - toast({ - title: 'Delegation Failed', - description: 'Failed to delegate signing key. Please try again.', - variant: 'destructive', - }); - return false; - } finally { - setIsDelegating(false); - } - }, - [isAuthenticated, verificationStatus, delegateKeyFromContext, toast] - ); - - // Clear delegation - const clearDelegation = useCallback(async (): Promise => { - const delegationInfo = await getDelegationStatus(); - if (!delegationInfo.isValid) { - toast({ - title: 'No Active Delegation', - description: 'There is no active key delegation to clear.', - }); - return true; - } - - try { - // Use the real clear implementation from AuthContext (includes toast) - await clearDelegationFromContext(); - return true; - } catch (error) { - console.error('Failed to clear delegation:', error); - toast({ - title: 'Clear Failed', - description: 'Failed to clear delegation. Please try again.', - variant: 'destructive', - }); - return false; - } - }, [getDelegationStatus, clearDelegationFromContext, toast]); - - // Renew delegation - const renewDelegation = useCallback( - async (duration: DelegationDuration): Promise => { - // Clear existing delegation first, then create new one - const cleared = await clearDelegation(); - if (!cleared) return false; - - return delegateKey(duration); - }, - [clearDelegation, delegateKey] - ); - - // Check verification status - const checkVerificationStatus = useCallback(async (): Promise => { - if (!isAuthenticated) return; - - try { - // This would check the current verification status - // The actual implementation would query the verification service - - toast({ - title: 'Status Updated', - description: 'Verification status has been refreshed.', - }); - } catch (error) { - console.error('Failed to check verification status:', error); - toast({ - title: 'Status Check Failed', - description: 'Failed to refresh verification status.', - variant: 'destructive', - }); - } - }, [isAuthenticated, toast]); - - return { - // States - isConnecting, - isVerifying: isVerifying || isAuthenticating, - isDelegating, - isDisconnecting, - - // Actions - connectWallet, - disconnectWallet, - verifyWallet, - delegateKey, - clearDelegation, - renewDelegation, - checkVerificationStatus, - }; -} diff --git a/src/hooks/actions/useForumActions.ts b/src/hooks/actions/useForumActions.ts deleted file mode 100644 index 96402a0..0000000 --- a/src/hooks/actions/useForumActions.ts +++ /dev/null @@ -1,646 +0,0 @@ -import { useCallback } from 'react'; -import { useForum } from '@/contexts/useForum'; -import { useAuth } from '@/hooks/core/useAuth'; -import { usePermissions } from '@/hooks/core/usePermissions'; -import { Cell, Post, Comment } from '@/types/forum'; -import { useToast } from '@/components/ui/use-toast'; - -export interface ForumActionStates { - isCreatingCell: boolean; - isCreatingPost: boolean; - isCreatingComment: boolean; - isVoting: boolean; - isModerating: boolean; -} - -export interface ForumActions extends ForumActionStates { - // Cell actions - createCell: ( - name: string, - description: string, - icon?: string - ) => Promise; - - // Post actions - createPost: ( - cellId: string, - title: string, - content: string - ) => Promise; - votePost: (postId: string, isUpvote: boolean) => Promise; - moderatePost: ( - cellId: string, - postId: string, - reason?: string - ) => Promise; - unmoderatePost: ( - cellId: string, - postId: string, - reason?: string - ) => Promise; - - // Comment actions - createComment: (postId: string, content: string) => Promise; - voteComment: (commentId: string, isUpvote: boolean) => Promise; - moderateComment: ( - cellId: string, - commentId: string, - reason?: string - ) => Promise; - unmoderateComment: ( - cellId: string, - commentId: string, - reason?: string - ) => Promise; - - // User moderation - moderateUser: ( - cellId: string, - userAddress: string, - reason?: string - ) => Promise; - unmoderateUser: ( - cellId: string, - userAddress: string, - reason?: string - ) => Promise; - - // Data refresh - refreshData: () => Promise; -} - -/** - * Hook for forum actions with loading states and error handling - */ -export function useForumActions(): ForumActions { - const { - createCell: baseCreateCell, - createPost: baseCreatePost, - createComment: baseCreateComment, - votePost: baseVotePost, - voteComment: baseVoteComment, - moderatePost: baseModeratePost, - unmoderatePost: baseUnmoderatePost, - moderateComment: baseModerateComment, - unmoderateComment: baseUnmoderateComment, - moderateUser: baseModerateUser, - unmoderateUser: baseUnmoderateUser, - refreshData: baseRefreshData, - isPostingCell, - isPostingPost, - isPostingComment, - isVoting, - getCellById, - } = useForum(); - - const { currentUser } = useAuth(); - const permissions = usePermissions(); - const { toast } = useToast(); - - // Cell creation - const createCell = useCallback( - async ( - name: string, - description: string, - icon?: string - ): Promise => { - if (!permissions.canCreateCell) { - toast({ - title: 'Permission Denied', - description: permissions.createCellReason, - variant: 'destructive', - }); - return null; - } - - if (!name.trim() || !description.trim()) { - toast({ - title: 'Invalid Input', - description: - 'Please provide both a name and description for the cell.', - variant: 'destructive', - }); - return null; - } - - try { - const result = await baseCreateCell(name, description, icon); - if (result) { - toast({ - title: 'Cell Created', - description: `Successfully created "${name}" cell.`, - }); - } - return result; - } catch { - toast({ - title: 'Creation Failed', - description: 'Failed to create cell. Please try again.', - variant: 'destructive', - }); - return null; - } - }, - [permissions.canCreateCell, baseCreateCell, toast] - ); - - // Post creation - const createPost = useCallback( - async ( - cellId: string, - title: string, - content: string - ): Promise => { - if (!permissions.canPost) { - toast({ - title: 'Permission Denied', - description: 'You need to verify Ordinal ownership to create posts.', - variant: 'destructive', - }); - return null; - } - - if (!title.trim() || !content.trim()) { - toast({ - title: 'Invalid Input', - description: 'Please provide both a title and content for the post.', - variant: 'destructive', - }); - return null; - } - - try { - const result = await baseCreatePost(cellId, title, content); - if (result) { - toast({ - title: 'Post Created', - description: `Successfully created "${title}".`, - }); - } - return result; - } catch { - toast({ - title: 'Creation Failed', - description: 'Failed to create post. Please try again.', - variant: 'destructive', - }); - return null; - } - }, - [permissions.canPost, baseCreatePost, toast] - ); - - // Comment creation - const createComment = useCallback( - async (postId: string, content: string): Promise => { - if (!permissions.canComment) { - toast({ - title: 'Permission Denied', - description: permissions.commentReason, - variant: 'destructive', - }); - return null; - } - - if (!content.trim()) { - toast({ - title: 'Invalid Input', - description: 'Please provide content for the comment.', - variant: 'destructive', - }); - return null; - } - - try { - const result = await baseCreateComment(postId, content); - if (result) { - toast({ - title: 'Comment Created', - description: 'Successfully posted your comment.', - }); - } - return result; - } catch { - toast({ - title: 'Creation Failed', - description: 'Failed to create comment. Please try again.', - variant: 'destructive', - }); - return null; - } - }, - [permissions.canComment, baseCreateComment, toast] - ); - - // Post voting - const votePost = useCallback( - async (postId: string, isUpvote: boolean): Promise => { - if (!permissions.canVote) { - toast({ - title: 'Permission Denied', - description: permissions.voteReason, - variant: 'destructive', - }); - return false; - } - - try { - const result = await baseVotePost(postId, isUpvote); - if (result) { - toast({ - title: 'Vote Recorded', - description: `Your ${isUpvote ? 'upvote' : 'downvote'} has been registered.`, - }); - } - return result; - } catch { - toast({ - title: 'Vote Failed', - description: 'Failed to record your vote. Please try again.', - variant: 'destructive', - }); - return false; - } - }, - [permissions.canVote, baseVotePost, toast] - ); - - // Comment voting - const voteComment = useCallback( - async (commentId: string, isUpvote: boolean): Promise => { - if (!permissions.canVote) { - toast({ - title: 'Permission Denied', - description: permissions.voteReason, - variant: 'destructive', - }); - return false; - } - - try { - const result = await baseVoteComment(commentId, isUpvote); - if (result) { - toast({ - title: 'Vote Recorded', - description: `Your ${isUpvote ? 'upvote' : 'downvote'} has been registered.`, - }); - } - return result; - } catch { - toast({ - title: 'Vote Failed', - description: 'Failed to record your vote. Please try again.', - variant: 'destructive', - }); - return false; - } - }, - [permissions.canVote, baseVoteComment, toast] - ); - - // Post moderation - const moderatePost = useCallback( - async ( - cellId: string, - postId: string, - reason?: string - ): Promise => { - const cell = getCellById(cellId); - const canModerate = - permissions.canModerate(cellId) && - cell && - currentUser?.address === cell.author; - - if (!canModerate) { - toast({ - title: 'Permission Denied', - description: 'You must be the cell owner to moderate content.', - variant: 'destructive', - }); - return false; - } - - try { - const result = await baseModeratePost( - cellId, - postId, - reason, - cell.author - ); - if (result) { - toast({ - title: 'Post Moderated', - description: 'The post has been moderated successfully.', - }); - } - return result; - } catch { - toast({ - title: 'Moderation Failed', - description: 'Failed to moderate post. Please try again.', - variant: 'destructive', - }); - return false; - } - }, - [permissions, currentUser, getCellById, baseModeratePost, toast] - ); - - // Post unmoderation - const unmoderatePost = useCallback( - async ( - cellId: string, - postId: string, - reason?: string - ): Promise => { - const cell = getCellById(cellId); - const canModerate = - permissions.canModerate(cellId) && - cell && - currentUser?.address === cell.author; - - if (!canModerate) { - toast({ - title: 'Permission Denied', - description: 'You must be the cell owner to unmoderate content.', - variant: 'destructive', - }); - return false; - } - - try { - const result = await baseUnmoderatePost( - cellId, - postId, - reason, - cell.author - ); - if (result) { - toast({ - title: 'Post Unmoderated', - description: 'The post is now visible again.', - }); - } - return result; - } catch { - toast({ - title: 'Unmoderation Failed', - description: 'Failed to unmoderate post. Please try again.', - variant: 'destructive', - }); - return false; - } - }, - [permissions, currentUser, getCellById, baseUnmoderatePost, toast] - ); - - // Comment moderation - const moderateComment = useCallback( - async ( - cellId: string, - commentId: string, - reason?: string - ): Promise => { - const cell = getCellById(cellId); - const canModerate = - permissions.canModerate(cellId) && - cell && - currentUser?.address === cell.author; - - if (!canModerate) { - toast({ - title: 'Permission Denied', - description: 'You must be the cell owner to moderate content.', - variant: 'destructive', - }); - return false; - } - - try { - const result = await baseModerateComment( - cellId, - commentId, - reason, - cell.author - ); - if (result) { - toast({ - title: 'Comment Moderated', - description: 'The comment has been moderated successfully.', - }); - } - return result; - } catch { - toast({ - title: 'Moderation Failed', - description: 'Failed to moderate comment. Please try again.', - variant: 'destructive', - }); - return false; - } - }, - [permissions, currentUser, getCellById, baseModerateComment, toast] - ); - - // Comment unmoderation - const unmoderateComment = useCallback( - async ( - cellId: string, - commentId: string, - reason?: string - ): Promise => { - const cell = getCellById(cellId); - const canModerate = - permissions.canModerate(cellId) && - cell && - currentUser?.address === cell.author; - - if (!canModerate) { - toast({ - title: 'Permission Denied', - description: 'You must be the cell owner to unmoderate content.', - variant: 'destructive', - }); - return false; - } - - try { - const result = await baseUnmoderateComment( - cellId, - commentId, - reason, - cell.author - ); - if (result) { - toast({ - title: 'Comment Unmoderated', - description: 'The comment is now visible again.', - }); - } - return result; - } catch { - toast({ - title: 'Unmoderation Failed', - description: 'Failed to unmoderate comment. Please try again.', - variant: 'destructive', - }); - return false; - } - }, - [permissions, currentUser, getCellById, baseUnmoderateComment, toast] - ); - - // User moderation - const moderateUser = useCallback( - async ( - cellId: string, - userAddress: string, - reason?: string - ): Promise => { - const cell = getCellById(cellId); - const canModerate = - permissions.canModerate(cellId) && - cell && - currentUser?.address === cell.author; - - if (!canModerate) { - toast({ - title: 'Permission Denied', - description: 'You must be the cell owner to moderate users.', - variant: 'destructive', - }); - return false; - } - - if (userAddress === currentUser?.address) { - toast({ - title: 'Invalid Action', - description: 'You cannot moderate yourself.', - variant: 'destructive', - }); - return false; - } - - try { - const result = await baseModerateUser( - cellId, - userAddress, - reason, - cell.author - ); - if (result) { - toast({ - title: 'User Moderated', - description: 'The user has been moderated successfully.', - }); - } - return result; - } catch { - toast({ - title: 'Moderation Failed', - description: 'Failed to moderate user. Please try again.', - variant: 'destructive', - }); - return false; - } - }, - [permissions, currentUser, getCellById, baseModerateUser, toast] - ); - - // User unmoderation - const unmoderateUser = useCallback( - async ( - cellId: string, - userAddress: string, - reason?: string - ): Promise => { - const cell = getCellById(cellId); - const canModerate = - permissions.canModerate(cellId) && - cell && - currentUser?.address === cell.author; - - if (!canModerate) { - toast({ - title: 'Permission Denied', - description: 'You must be the cell owner to unmoderate users.', - variant: 'destructive', - }); - return false; - } - - if (userAddress === currentUser?.address) { - toast({ - title: 'Invalid Action', - description: 'You cannot unmoderate yourself.', - variant: 'destructive', - }); - return false; - } - - try { - const result = await baseUnmoderateUser( - cellId, - userAddress, - reason, - cell.author - ); - if (result) { - toast({ - title: 'User Unmoderated', - description: 'The user is now unmoderated in this cell.', - }); - } - return result; - } catch { - toast({ - title: 'Unmoderation Failed', - description: 'Failed to unmoderate user. Please try again.', - variant: 'destructive', - }); - return false; - } - }, - [permissions, currentUser, getCellById, baseUnmoderateUser, toast] - ); - - // Data refresh - const refreshData = useCallback(async (): Promise => { - try { - await baseRefreshData(); - toast({ - title: 'Data Refreshed', - description: 'Forum data has been updated.', - }); - } catch { - toast({ - title: 'Refresh Failed', - description: 'Failed to refresh data. Please try again.', - variant: 'destructive', - }); - } - }, [baseRefreshData, toast]); - - return { - // States - isCreatingCell: isPostingCell, - isCreatingPost: isPostingPost, - isCreatingComment: isPostingComment, - isVoting, - isModerating: false, // This would need to be added to the context - - // Actions - createCell, - createPost, - createComment, - votePost, - voteComment, - moderatePost, - unmoderatePost, - moderateComment, - unmoderateComment, - moderateUser, - unmoderateUser, - refreshData, - }; -} diff --git a/src/hooks/actions/useUserActions.ts b/src/hooks/actions/useUserActions.ts deleted file mode 100644 index c7261fd..0000000 --- a/src/hooks/actions/useUserActions.ts +++ /dev/null @@ -1,283 +0,0 @@ -import { useCallback, useState } from 'react'; -import { useForum } from '@/contexts/useForum'; -import { useAuth } from '@/hooks/core/useAuth'; -import { usePermissions } from '@/hooks/core/usePermissions'; -import { EDisplayPreference } from '@/types/identity'; -import { useToast } from '@/components/ui/use-toast'; - -export interface UserActionStates { - isUpdatingProfile: boolean; - isUpdatingCallSign: boolean; - isUpdatingDisplayPreference: boolean; -} - -export interface UserActions extends UserActionStates { - updateCallSign: (callSign: string) => Promise; - updateDisplayPreference: (preference: EDisplayPreference) => Promise; - updateProfile: (updates: { - callSign?: string; - displayPreference?: EDisplayPreference; - }) => Promise; - clearCallSign: () => Promise; -} - -/** - * Hook for user profile and identity actions - */ -export function useUserActions(): UserActions { - const { userIdentityService } = useForum(); - const { currentUser } = useAuth(); - const permissions = usePermissions(); - const { toast } = useToast(); - - const [isUpdatingProfile, setIsUpdatingProfile] = useState(false); - const [isUpdatingCallSign, setIsUpdatingCallSign] = useState(false); - const [isUpdatingDisplayPreference, setIsUpdatingDisplayPreference] = - useState(false); - - // Update call sign - const updateCallSign = useCallback( - async (callSign: string): Promise => { - if (!permissions.canUpdateProfile) { - toast({ - title: 'Permission Denied', - description: - 'You need to connect your wallet to update your profile.', - variant: 'destructive', - }); - return false; - } - - if (!userIdentityService || !currentUser) { - toast({ - title: 'Service Unavailable', - description: 'User identity service is not available.', - variant: 'destructive', - }); - return false; - } - - if (!callSign.trim()) { - toast({ - title: 'Invalid Input', - description: 'Call sign cannot be empty.', - variant: 'destructive', - }); - return false; - } - - // Basic validation for call sign - if (callSign.length < 3 || callSign.length > 20) { - toast({ - title: 'Invalid Call Sign', - description: 'Call sign must be between 3 and 20 characters.', - variant: 'destructive', - }); - return false; - } - - if (!/^[a-zA-Z0-9_-]+$/.test(callSign)) { - toast({ - title: 'Invalid Call Sign', - description: - 'Call sign can only contain letters, numbers, underscores, and hyphens.', - variant: 'destructive', - }); - return false; - } - - setIsUpdatingCallSign(true); - - try { - const success = await userIdentityService.updateUserProfile( - currentUser.address, - callSign, - currentUser.displayPreference - ); - - if (success) { - toast({ - title: 'Call Sign Updated', - description: `Your call sign has been set to "${callSign}".`, - }); - return true; - } else { - toast({ - title: 'Update Failed', - description: 'Failed to update call sign. Please try again.', - variant: 'destructive', - }); - return false; - } - } catch (error) { - console.error('Failed to update call sign:', error); - toast({ - title: 'Update Failed', - description: 'An error occurred while updating your call sign.', - variant: 'destructive', - }); - return false; - } finally { - setIsUpdatingCallSign(false); - } - }, - [permissions.canUpdateProfile, userIdentityService, currentUser, toast] - ); - - // Update display preference - const updateDisplayPreference = useCallback( - async (preference: EDisplayPreference): Promise => { - if (!permissions.canUpdateProfile) { - toast({ - title: 'Permission Denied', - description: - 'You need to connect your wallet to update your profile.', - variant: 'destructive', - }); - return false; - } - - if (!userIdentityService || !currentUser) { - toast({ - title: 'Service Unavailable', - description: 'User identity service is not available.', - variant: 'destructive', - }); - return false; - } - - setIsUpdatingDisplayPreference(true); - - try { - const success = await userIdentityService.updateUserProfile( - currentUser.address, - currentUser.callSign || '', - preference - ); - - if (success) { - const preferenceLabel = - preference === EDisplayPreference.CALL_SIGN - ? 'Call Sign' - : 'Wallet Address'; - - toast({ - title: 'Display Preference Updated', - description: `Your display preference has been set to "${preferenceLabel}".`, - }); - return true; - } else { - toast({ - title: 'Update Failed', - description: - 'Failed to update display preference. Please try again.', - variant: 'destructive', - }); - return false; - } - } catch (error) { - console.error('Failed to update display preference:', error); - toast({ - title: 'Update Failed', - description: - 'An error occurred while updating your display preference.', - variant: 'destructive', - }); - return false; - } finally { - setIsUpdatingDisplayPreference(false); - } - }, - [permissions.canUpdateProfile, userIdentityService, currentUser, toast] - ); - - // Update profile (multiple fields at once) - const updateProfile = useCallback( - async (updates: { - callSign?: string; - displayPreference?: EDisplayPreference; - }): Promise => { - if (!permissions.canUpdateProfile) { - toast({ - title: 'Permission Denied', - description: - 'You need to connect your wallet to update your profile.', - variant: 'destructive', - }); - return false; - } - - if (!userIdentityService || !currentUser) { - toast({ - title: 'Service Unavailable', - description: 'User identity service is not available.', - variant: 'destructive', - }); - return false; - } - - setIsUpdatingProfile(true); - - try { - let success = true; - if ( - updates.callSign !== undefined || - updates.displayPreference !== undefined - ) { - const callSignToSend = updates.callSign; - const preferenceToSend = - updates.displayPreference ?? currentUser.displayPreference; - success = await userIdentityService.updateUserProfile( - currentUser.address, - callSignToSend, - preferenceToSend - ); - } - - if (success) { - toast({ - title: 'Profile Updated', - description: 'Your profile has been updated successfully.', - }); - return true; - } else { - toast({ - title: 'Update Failed', - description: 'Some profile updates failed. Please try again.', - variant: 'destructive', - }); - return false; - } - } catch (error) { - console.error('Failed to update profile:', error); - toast({ - title: 'Update Failed', - description: 'An error occurred while updating your profile.', - variant: 'destructive', - }); - return false; - } finally { - setIsUpdatingProfile(false); - } - }, - [permissions.canUpdateProfile, userIdentityService, currentUser, toast] - ); - - // Clear call sign - const clearCallSign = useCallback(async (): Promise => { - return updateCallSign(''); - }, [updateCallSign]); - - return { - // States - isUpdatingProfile, - isUpdatingCallSign, - isUpdatingDisplayPreference, - - // Actions - updateCallSign, - updateDisplayPreference, - updateProfile, - clearCallSign, - }; -} diff --git a/src/hooks/core/useAuth.ts b/src/hooks/core/useAuth.ts deleted file mode 100644 index 4ba7a1c..0000000 --- a/src/hooks/core/useAuth.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { useAuth as useBaseAuth } from '@/contexts/useAuth'; -import { useForum } from '@/contexts/useForum'; -import { User, EVerificationStatus } from '@/types/identity'; - -export interface AuthState { - currentUser: User | null; - isAuthenticated: boolean; - isAuthenticating: boolean; - verificationStatus: EVerificationStatus; - - // Helper functions - getDisplayName: () => string; - getVerificationBadge: () => string | null; -} - -/** - * Unified authentication hook that provides core auth state and helpers - */ -export function useAuth(): AuthState { - const { currentUser, isAuthenticated, isAuthenticating, verificationStatus } = - useBaseAuth(); - const { userIdentityService } = useForum(); - - // Helper functions - const getDisplayName = (): string => { - if (!currentUser) return 'Anonymous'; - // Centralized display logic; fallback to truncated address if service unavailable - if (userIdentityService) { - return userIdentityService.getDisplayName(currentUser.address); - } - const addr = currentUser.address; - return `${addr.slice(0, 6)}...${addr.slice(-4)}`; - }; - - const getVerificationBadge = (): string | null => { - switch (verificationStatus) { - case EVerificationStatus.ENS_ORDINAL_VERIFIED: - return '🔑'; // ENS/Ordinal owner - case EVerificationStatus.WALLET_CONNECTED: - return '✅'; // Wallet connected - default: - return null; - } - }; - - return { - currentUser, - isAuthenticated, - isAuthenticating, - verificationStatus, - getDisplayName, - getVerificationBadge, - }; -} diff --git a/src/hooks/core/useBookmarks.ts b/src/hooks/core/useBookmarks.ts deleted file mode 100644 index 1fcfd2d..0000000 --- a/src/hooks/core/useBookmarks.ts +++ /dev/null @@ -1,256 +0,0 @@ -import { useState, useEffect, useCallback } from 'react'; -import { Bookmark, BookmarkType, Post, Comment } from '@/types/forum'; -import { BookmarkService } from '@/lib/services/BookmarkService'; -import { useAuth } from '@/contexts/useAuth'; - -/** - * Hook for managing bookmarks - * Provides bookmark state and operations for the current user - */ -export function useBookmarks() { - const { currentUser } = useAuth(); - const [bookmarks, setBookmarks] = useState([]); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - - const loadBookmarks = useCallback(async () => { - if (!currentUser?.address) return; - - setLoading(true); - setError(null); - try { - const userBookmarks = await BookmarkService.getUserBookmarks( - currentUser.address - ); - setBookmarks(userBookmarks); - } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to load bookmarks'); - } finally { - setLoading(false); - } - }, [currentUser?.address]); - - // Load user bookmarks when user changes - useEffect(() => { - if (currentUser?.address) { - loadBookmarks(); - } else { - setBookmarks([]); - } - }, [currentUser?.address, loadBookmarks]); - - const bookmarkPost = useCallback( - async (post: Post, cellId?: string) => { - if (!currentUser?.address) return false; - - try { - const isBookmarked = await BookmarkService.togglePostBookmark( - post, - currentUser.address, - cellId - ); - await loadBookmarks(); // Refresh the list - return isBookmarked; - } catch (err) { - setError( - err instanceof Error ? err.message : 'Failed to bookmark post' - ); - return false; - } - }, - [currentUser?.address, loadBookmarks] - ); - - const bookmarkComment = useCallback( - async (comment: Comment, postId?: string) => { - if (!currentUser?.address) return false; - - try { - const isBookmarked = await BookmarkService.toggleCommentBookmark( - comment, - currentUser.address, - postId - ); - await loadBookmarks(); // Refresh the list - return isBookmarked; - } catch (err) { - setError( - err instanceof Error ? err.message : 'Failed to bookmark comment' - ); - return false; - } - }, - [currentUser?.address, loadBookmarks] - ); - - const removeBookmark = useCallback( - async (bookmarkId: string) => { - try { - const bookmark = BookmarkService.getBookmark(bookmarkId); - if (bookmark) { - await BookmarkService.removeBookmark( - bookmark.type, - bookmark.targetId - ); - await loadBookmarks(); // Refresh the list - } - } catch (err) { - setError( - err instanceof Error ? err.message : 'Failed to remove bookmark' - ); - } - }, - [loadBookmarks] - ); - - const isPostBookmarked = useCallback( - (postId: string) => { - if (!currentUser?.address) return false; - return BookmarkService.isPostBookmarked(currentUser.address, postId); - }, - [currentUser?.address] - ); - - const isCommentBookmarked = useCallback( - (commentId: string) => { - if (!currentUser?.address) return false; - return BookmarkService.isCommentBookmarked( - currentUser.address, - commentId - ); - }, - [currentUser?.address] - ); - - const getBookmarksByType = useCallback( - (type: BookmarkType) => { - return bookmarks.filter(bookmark => bookmark.type === type); - }, - [bookmarks] - ); - - const clearAllBookmarks = useCallback(async () => { - if (!currentUser?.address) return; - - try { - await BookmarkService.clearUserBookmarks(currentUser.address); - setBookmarks([]); - } catch (err) { - setError( - err instanceof Error ? err.message : 'Failed to clear bookmarks' - ); - } - }, [currentUser?.address]); - - return { - bookmarks, - loading, - error, - bookmarkPost, - bookmarkComment, - removeBookmark, - isPostBookmarked, - isCommentBookmarked, - getBookmarksByType, - clearAllBookmarks, - refreshBookmarks: loadBookmarks, - }; -} - -/** - * Hook for bookmarking a specific post - * Provides bookmark state and toggle function for a single post - */ -export function usePostBookmark(post: Post | null, cellId?: string) { - const { currentUser } = useAuth(); - const [isBookmarked, setIsBookmarked] = useState(false); - const [loading, setLoading] = useState(false); - - // Check initial bookmark status - useEffect(() => { - if (currentUser?.address && post?.id) { - const bookmarked = BookmarkService.isPostBookmarked( - currentUser.address, - post.id - ); - setIsBookmarked(bookmarked); - } else { - setIsBookmarked(false); - } - }, [currentUser?.address, post?.id]); - - const toggleBookmark = useCallback(async () => { - if (!currentUser?.address || !post) return false; - - setLoading(true); - try { - const newBookmarkStatus = await BookmarkService.togglePostBookmark( - post, - currentUser.address, - cellId - ); - setIsBookmarked(newBookmarkStatus); - return newBookmarkStatus; - } catch (err) { - console.error('Failed to toggle post bookmark:', err); - return false; - } finally { - setLoading(false); - } - }, [currentUser?.address, post, cellId]); - - return { - isBookmarked, - loading, - toggleBookmark, - }; -} - -/** - * Hook for bookmarking a specific comment - * Provides bookmark state and toggle function for a single comment - */ -export function useCommentBookmark(comment: Comment, postId?: string) { - const { currentUser } = useAuth(); - const [isBookmarked, setIsBookmarked] = useState(false); - const [loading, setLoading] = useState(false); - - // Check initial bookmark status - useEffect(() => { - if (currentUser?.address) { - const bookmarked = BookmarkService.isCommentBookmarked( - currentUser.address, - comment.id - ); - setIsBookmarked(bookmarked); - } else { - setIsBookmarked(false); - } - }, [currentUser?.address, comment.id]); - - const toggleBookmark = useCallback(async () => { - if (!currentUser?.address) return false; - - setLoading(true); - try { - const newBookmarkStatus = await BookmarkService.toggleCommentBookmark( - comment, - currentUser.address, - postId - ); - setIsBookmarked(newBookmarkStatus); - return newBookmarkStatus; - } catch (err) { - console.error('Failed to toggle comment bookmark:', err); - return false; - } finally { - setLoading(false); - } - }, [currentUser?.address, comment, postId]); - - return { - isBookmarked, - loading, - toggleBookmark, - }; -} diff --git a/src/hooks/core/useEnhancedUserDisplay.ts b/src/hooks/core/useEnhancedUserDisplay.ts deleted file mode 100644 index bdad558..0000000 --- a/src/hooks/core/useEnhancedUserDisplay.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { useState, useEffect, useMemo } from 'react'; -import { useForum } from '@/contexts/useForum'; -import { EDisplayPreference, EVerificationStatus } from '@/types/identity'; - -export interface UserDisplayInfo { - displayName: string; - callSign: string | null; - ensName: string | null; - ordinalDetails: string | null; - verificationLevel: EVerificationStatus; - displayPreference: EDisplayPreference | null; - isLoading: boolean; - error: string | null; -} - -/** - * Enhanced user display hook with caching and reactive updates - */ -export function useEnhancedUserDisplay(address: string): UserDisplayInfo { - const { userIdentityService, userVerificationStatus } = useForum(); - const [displayInfo, setDisplayInfo] = useState({ - displayName: `${address.slice(0, 6)}...${address.slice(-4)}`, - callSign: null, - ensName: null, - ordinalDetails: null, - verificationLevel: EVerificationStatus.WALLET_UNCONNECTED, - displayPreference: null, - isLoading: true, - error: null, - }); - const [refreshTrigger, setRefreshTrigger] = useState(0); - - // Get verification status from forum context for reactive updates - const verificationInfo = useMemo(() => { - return ( - userVerificationStatus[address] || { - isVerified: false, - ensName: null, - verificationStatus: EVerificationStatus.WALLET_UNCONNECTED, - } - ); - }, [userVerificationStatus, address]); - - // Set up refresh listener for user identity changes - useEffect(() => { - if (!userIdentityService || !address) return; - - const unsubscribe = userIdentityService.addRefreshListener( - updatedAddress => { - if (updatedAddress === address) { - setRefreshTrigger(prev => prev + 1); - } - } - ); - - return unsubscribe; - }, [userIdentityService, address]); - - useEffect(() => { - const getUserDisplayInfo = async () => { - if (!address) { - setDisplayInfo(prev => ({ - ...prev, - isLoading: false, - error: 'No address provided', - })); - return; - } - - if (!userIdentityService) { - console.log( - 'useEnhancedUserDisplay: No service available, using fallback', - { address } - ); - setDisplayInfo({ - displayName: `${address.slice(0, 6)}...${address.slice(-4)}`, - callSign: null, - ensName: verificationInfo.ensName || null, - ordinalDetails: null, - verificationLevel: - verificationInfo.verificationStatus || - EVerificationStatus.WALLET_UNCONNECTED, - displayPreference: null, - isLoading: false, - error: null, - }); - return; - } - - try { - const identity = await userIdentityService.getUserIdentity(address); - - if (identity) { - const displayName = userIdentityService.getDisplayName(address); - - setDisplayInfo({ - displayName, - callSign: identity.callSign || null, - ensName: identity.ensName || null, - ordinalDetails: identity.ordinalDetails - ? identity.ordinalDetails.ordinalDetails - : null, - verificationLevel: identity.verificationStatus, - displayPreference: identity.displayPreference || null, - isLoading: false, - error: null, - }); - } else { - setDisplayInfo({ - displayName: userIdentityService.getDisplayName(address), - callSign: null, - ensName: verificationInfo.ensName || null, - ordinalDetails: null, - verificationLevel: - verificationInfo.verificationStatus || - EVerificationStatus.WALLET_UNCONNECTED, - displayPreference: null, - isLoading: false, - error: null, - }); - } - } catch (error) { - console.error( - 'useEnhancedUserDisplay: Failed to get user display info:', - error - ); - setDisplayInfo({ - displayName: `${address.slice(0, 6)}...${address.slice(-4)}`, - callSign: null, - ensName: null, - ordinalDetails: null, - verificationLevel: EVerificationStatus.WALLET_UNCONNECTED, - displayPreference: null, - isLoading: false, - error: error instanceof Error ? error.message : 'Unknown error', - }); - } - }; - - getUserDisplayInfo(); - }, [address, userIdentityService, verificationInfo, refreshTrigger]); - - // Update display info when verification status changes reactively - useEffect(() => { - if (!displayInfo.isLoading && verificationInfo) { - setDisplayInfo(prev => ({ - ...prev, - ensName: verificationInfo.ensName || prev.ensName, - verificationLevel: - verificationInfo.verificationStatus || prev.verificationLevel, - })); - } - }, [ - verificationInfo.ensName, - verificationInfo.verificationStatus, - displayInfo.isLoading, - verificationInfo, - ]); - - return displayInfo; -} - -// Export as the main useUserDisplay hook -export { useEnhancedUserDisplay as useUserDisplay }; diff --git a/src/hooks/core/useForumData.ts b/src/hooks/core/useForumData.ts deleted file mode 100644 index 8da6ff0..0000000 --- a/src/hooks/core/useForumData.ts +++ /dev/null @@ -1,388 +0,0 @@ -import { useMemo } from 'react'; -import { useForum } from '@/contexts/useForum'; -import { useAuth } from '@/contexts/useAuth'; -import { useModeration } from '@/contexts/ModerationContext'; -import { Cell, Post, Comment, UserVerificationStatus } from '@/types/forum'; -import { EVerificationStatus } from '@/types/identity'; - -export interface CellWithStats extends Cell { - postCount: number; - activeUsers: number; - recentActivity: number; -} - -export interface PostWithVoteStatus extends Post { - userUpvoted: boolean; - userDownvoted: boolean; - voteScore: number; - canVote: boolean; - canModerate: boolean; -} - -export interface CommentWithVoteStatus extends Comment { - userUpvoted: boolean; - userDownvoted: boolean; - voteScore: number; - canVote: boolean; - canModerate: boolean; -} - -export interface ForumData { - // Raw data - cells: Cell[]; - posts: Post[]; - comments: Comment[]; - userVerificationStatus: UserVerificationStatus; - - // Loading states - isInitialLoading: boolean; - isRefreshing: boolean; - isNetworkConnected: boolean; - error: string | null; - - // Computed data with reactive updates - cellsWithStats: CellWithStats[]; - postsWithVoteStatus: PostWithVoteStatus[]; - commentsWithVoteStatus: CommentWithVoteStatus[]; - - // Filtered data based on moderation settings - filteredPosts: PostWithVoteStatus[]; - filteredComments: CommentWithVoteStatus[]; - filteredCellsWithStats: CellWithStats[]; - filteredCommentsByPost: Record; - - // Organized data - postsByCell: Record; - commentsByPost: Record; - - // User-specific data - userVotedPosts: Set; - userVotedComments: Set; - userCreatedPosts: Set; - userCreatedComments: Set; -} - -/** - * Main forum data hook with reactive updates and computed properties - * This is the primary data source for all forum-related information - */ -export function useForumData(): ForumData { - const { - cells, - posts, - comments, - userVerificationStatus, - isInitialLoading, - isRefreshing, - isNetworkConnected, - error, - } = useForum(); - - const { currentUser } = useAuth(); - const { showModerated } = useModeration(); - - // Compute cells with statistics - const cellsWithStats = useMemo((): CellWithStats[] => { - return cells.map(cell => { - const cellPosts = posts.filter(post => post.cellId === cell.id); - const recentPosts = cellPosts.filter( - post => Date.now() - post.timestamp < 7 * 24 * 60 * 60 * 1000 // 7 days - ); - - const uniqueAuthors = new Set(cellPosts.map(post => post.author)); - - return { - ...cell, - postCount: cellPosts.length, - activeUsers: uniqueAuthors.size, - recentActivity: recentPosts.length, - }; - }); - }, [cells, posts]); - - // Helper function to check if user can vote - const canUserVote = useMemo(() => { - if (!currentUser) return false; - - return ( - currentUser.verificationStatus === - EVerificationStatus.ENS_ORDINAL_VERIFIED || - currentUser.verificationStatus === EVerificationStatus.WALLET_CONNECTED || - Boolean(currentUser.ensDetails) || - Boolean(currentUser.ordinalDetails) - ); - }, [currentUser]); - - // Helper function to check if user can moderate in a cell - const canUserModerate = useMemo(() => { - const moderationMap: Record = {}; - - if (!currentUser) return moderationMap; - - cells.forEach(cell => { - moderationMap[cell.id] = currentUser.address === cell.signature; - }); - - return moderationMap; - }, [currentUser, cells]); - - // Compute posts with vote status - const postsWithVoteStatus = useMemo((): PostWithVoteStatus[] => { - return posts.map(post => { - const userUpvoted = currentUser - ? post.upvotes.some(vote => vote.author === currentUser.address) - : false; - - const userDownvoted = currentUser - ? post.downvotes.some(vote => vote.author === currentUser.address) - : false; - - const voteScore = post.upvotes.length - post.downvotes.length; - const canModerate = canUserModerate[post.cellId] || false; - - return { - ...post, - userUpvoted, - userDownvoted, - voteScore, - canVote: canUserVote, - canModerate, - }; - }); - }, [posts, currentUser, canUserVote, canUserModerate]); - - // Compute comments with vote status - const commentsWithVoteStatus = useMemo((): CommentWithVoteStatus[] => { - return comments.map(comment => { - const userUpvoted = currentUser - ? comment.upvotes.some(vote => vote.author === currentUser.address) - : false; - - const userDownvoted = currentUser - ? comment.downvotes.some(vote => vote.author === currentUser.address) - : false; - - const voteScore = comment.upvotes.length - comment.downvotes.length; - - // Find the post to determine cell for moderation - const parentPost = posts.find(post => post.id === comment.postId); - const canModerate = parentPost - ? canUserModerate[parentPost.cellId] || false - : false; - - return { - ...comment, - userUpvoted, - userDownvoted, - voteScore, - canVote: canUserVote, - canModerate, - }; - }); - }, [comments, currentUser, canUserVote, canUserModerate, posts]); - - // Organize posts by cell - const postsByCell = useMemo((): Record => { - const organized: Record = {}; - - postsWithVoteStatus.forEach(post => { - if (!organized[post.cellId]) { - organized[post.cellId] = []; - } - const cellPosts = organized[post.cellId]; - if (cellPosts) { - cellPosts.push(post); - } - }); - - // Sort posts within each cell by relevance score or timestamp - Object.keys(organized).forEach(cellId => { - const cellPosts = organized[cellId]; - if (cellPosts) { - cellPosts.sort((a, b) => { - if ( - a.relevanceScore !== undefined && - b.relevanceScore !== undefined - ) { - return b.relevanceScore - a.relevanceScore; - } - return b.timestamp - a.timestamp; - }); - } - }); - - return organized; - }, [postsWithVoteStatus]); - - // Organize comments by post - const commentsByPost = useMemo((): Record< - string, - CommentWithVoteStatus[] - > => { - const organized: Record = {}; - - commentsWithVoteStatus.forEach(comment => { - if (!organized[comment.postId]) { - organized[comment.postId] = []; - } - const postComments = organized[comment.postId]; - if (postComments) { - postComments.push(comment); - } - }); - - // Sort comments within each post by timestamp (oldest first) - Object.keys(organized).forEach(postId => { - const postComments = organized[postId]; - if (postComments) { - postComments.sort((a, b) => a.timestamp - b.timestamp); - } - }); - - return organized; - }, [commentsWithVoteStatus]); - - // User-specific data sets - const userVotedPosts = useMemo(() => { - const votedPosts = new Set(); - if (!currentUser) return votedPosts; - - postsWithVoteStatus.forEach(post => { - if (post.userUpvoted || post.userDownvoted) { - votedPosts.add(post.id); - } - }); - - return votedPosts; - }, [postsWithVoteStatus, currentUser]); - - const userVotedComments = useMemo(() => { - const votedComments = new Set(); - if (!currentUser) return votedComments; - - commentsWithVoteStatus.forEach(comment => { - if (comment.userUpvoted || comment.userDownvoted) { - votedComments.add(comment.id); - } - }); - - return votedComments; - }, [commentsWithVoteStatus, currentUser]); - - const userCreatedPosts = useMemo(() => { - const createdPosts = new Set(); - if (!currentUser) return createdPosts; - - posts.forEach(post => { - if (post.author === currentUser.address) { - createdPosts.add(post.id); - } - }); - - return createdPosts; - }, [posts, currentUser]); - - const userCreatedComments = useMemo(() => { - const createdComments = new Set(); - if (!currentUser) return createdComments; - - comments.forEach(comment => { - if (comment.author === currentUser.address) { - createdComments.add(comment.id); - } - }); - - return createdComments; - }, [comments, currentUser]); - - // Filtered data based on moderation settings - const filteredPosts = useMemo(() => { - return showModerated - ? postsWithVoteStatus - : postsWithVoteStatus.filter(post => !post.moderated); - }, [postsWithVoteStatus, showModerated]); - - const filteredComments = useMemo(() => { - if (showModerated) return commentsWithVoteStatus; - - // Hide moderated comments AND comments whose parent post is moderated - const moderatedPostIds = new Set( - postsWithVoteStatus.filter(p => p.moderated).map(p => p.id) - ); - - return commentsWithVoteStatus.filter( - comment => !comment.moderated && !moderatedPostIds.has(comment.postId) - ); - }, [commentsWithVoteStatus, postsWithVoteStatus, showModerated]); - - // Filtered cells with stats based on filtered posts - const filteredCellsWithStats = useMemo((): CellWithStats[] => { - return cells.map(cell => { - const cellPosts = filteredPosts.filter(post => post.cellId === cell.id); - const recentPosts = cellPosts.filter( - post => Date.now() - post.timestamp < 7 * 24 * 60 * 60 * 1000 // 7 days - ); - - const uniqueAuthors = new Set(cellPosts.map(post => post.author)); - - return { - ...cell, - postCount: cellPosts.length, - activeUsers: uniqueAuthors.size, - recentActivity: recentPosts.length, - }; - }); - }, [cells, filteredPosts]); - - // Filtered comments organized by post - const filteredCommentsByPost = useMemo((): Record< - string, - CommentWithVoteStatus[] - > => { - const organized: Record = {}; - - filteredComments.forEach(comment => { - if (!organized[comment.postId]) { - organized[comment.postId] = []; - } - organized[comment.postId]!.push(comment); - }); - - return organized; - }, [filteredComments]); - - return { - // Raw data - cells, - posts, - comments, - userVerificationStatus, - - // Loading states - isInitialLoading, - isRefreshing, - isNetworkConnected, - error, - - // Computed data - cellsWithStats, - postsWithVoteStatus, - commentsWithVoteStatus, - - // Filtered data based on moderation settings - filteredPosts, - filteredComments, - filteredCellsWithStats, - filteredCommentsByPost, - - // Organized data - postsByCell, - commentsByPost, - - // User-specific data - userVotedPosts, - userVotedComments, - userCreatedPosts, - userCreatedComments, - }; -} diff --git a/src/hooks/core/usePermissions.ts b/src/hooks/core/usePermissions.ts deleted file mode 100644 index 491b489..0000000 --- a/src/hooks/core/usePermissions.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { useMemo } from 'react'; -import { useAuth } from './useAuth'; -import { useForumData } from './useForumData'; -import { EVerificationStatus } from '@/types/identity'; - -export interface Permission { - canPost: boolean; - canComment: boolean; - canVote: boolean; - canCreateCell: boolean; - canModerate: (cellId: string) => boolean; - canDelegate: boolean; - canUpdateProfile: boolean; -} - -export interface PermissionReasons { - voteReason: string; - postReason: string; - commentReason: string; - createCellReason: string; - moderateReason: (cellId: string) => string; -} - -export interface PermissionResult { - allowed: boolean; - reason: string; -} - -/** - * Unified permission system with single source of truth for all permission logic - */ -export function usePermissions(): Permission & - PermissionReasons & { - checkPermission: ( - action: keyof Permission, - cellId?: string - ) => PermissionResult; - } { - const { currentUser, verificationStatus } = useAuth(); - const { cellsWithStats } = useForumData(); - - // Single source of truth for all permission logic - const permissions = useMemo((): Permission => { - const isWalletConnected = - verificationStatus === EVerificationStatus.WALLET_CONNECTED; - const isVerified = - verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED; - - return { - canPost: isWalletConnected || isVerified, - canComment: isWalletConnected || isVerified, - canVote: isWalletConnected || isVerified, - canCreateCell: isVerified, // Only ENS/Ordinal owners - canModerate: (cellId: string) => { - if (!currentUser || !cellId) return false; - // Check if user is the creator of the cell - const cell = cellsWithStats.find(c => c.id === cellId); - return cell ? cell.author === currentUser.address : false; - }, - canDelegate: isWalletConnected || isVerified, - canUpdateProfile: Boolean(currentUser), - }; - }, [currentUser, verificationStatus, cellsWithStats]); - - // Single source of truth for permission reasons - const reasons = useMemo((): PermissionReasons => { - if (!currentUser) { - return { - voteReason: 'Connect your wallet to vote', - postReason: 'Connect your wallet to post', - commentReason: 'Connect your wallet to comment', - createCellReason: 'Connect your wallet to create cells', - moderateReason: () => 'Connect your wallet to moderate', - }; - } - - return { - voteReason: permissions.canVote - ? 'You can vote' - : 'Connect your wallet to vote', - postReason: permissions.canPost - ? 'You can post' - : 'Connect your wallet to post', - commentReason: permissions.canComment - ? 'You can comment' - : 'Connect your wallet to comment', - createCellReason: permissions.canCreateCell - ? 'You can create cells' - : 'Verify ENS or Logos ordinal to create cells', - moderateReason: (cellId: string) => { - if (!cellId) return 'Cell ID required'; - return permissions.canModerate(cellId) - ? 'You can moderate this cell' - : 'Only cell creators can moderate'; - }, - }; - }, [currentUser, verificationStatus, permissions]); - - // Unified permission checker - const checkPermission = useMemo(() => { - return (action: keyof Permission, cellId?: string): PermissionResult => { - let allowed = false; - let reason = ''; - - switch (action) { - case 'canVote': - allowed = permissions.canVote; - reason = reasons.voteReason; - break; - - case 'canPost': - allowed = permissions.canPost; - reason = reasons.postReason; - break; - - case 'canComment': - allowed = permissions.canComment; - reason = reasons.commentReason; - break; - - case 'canCreateCell': - allowed = permissions.canCreateCell; - reason = reasons.createCellReason; - break; - - case 'canModerate': - allowed = cellId ? permissions.canModerate(cellId) : false; - reason = cellId ? reasons.moderateReason(cellId) : 'Cell ID required'; - break; - - case 'canDelegate': - allowed = permissions.canDelegate; - reason = allowed - ? 'You can delegate keys' - : 'Connect your wallet to delegate keys'; - break; - - case 'canUpdateProfile': - allowed = permissions.canUpdateProfile; - reason = allowed - ? 'You can update your profile' - : 'Connect your wallet to update profile'; - break; - - default: - allowed = false; - reason = 'Unknown permission'; - } - - return { allowed, reason }; - }; - }, [permissions, reasons]); - - return { - ...permissions, - ...reasons, - checkPermission, - }; -} diff --git a/src/hooks/core/useUserDisplay.ts b/src/hooks/core/useUserDisplay.ts deleted file mode 100644 index db3efe9..0000000 --- a/src/hooks/core/useUserDisplay.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Re-export the enhanced user display hook as the main useUserDisplay -export { useEnhancedUserDisplay as useUserDisplay } from './useEnhancedUserDisplay'; -export type { UserDisplayInfo } from './useEnhancedUserDisplay'; diff --git a/src/hooks/derived/useCell.ts b/src/hooks/derived/useCell.ts deleted file mode 100644 index 07f7b13..0000000 --- a/src/hooks/derived/useCell.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { useMemo } from 'react'; -import { useForumData, CellWithStats } from '@/hooks/core/useForumData'; -import { useAuth } from '@/hooks/core/useAuth'; -import { EVerificationStatus } from '@/types/identity'; - -export interface CellData extends CellWithStats { - posts: Array<{ - id: string; - title: string; - content: string; - author: string; - timestamp: number; - voteScore: number; - commentCount: number; - }>; - isUserAdmin: boolean; - canModerate: boolean; - canPost: boolean; -} - -/** - * Hook for getting a specific cell with its posts and permissions - */ -export function useCell(cellId: string | undefined): CellData | null { - const { cellsWithStats, postsByCell, commentsByPost } = useForumData(); - const { currentUser } = useAuth(); - - return useMemo(() => { - if (!cellId) return null; - - const cell = cellsWithStats.find(c => c.id === cellId); - if (!cell) return null; - - const cellPosts = postsByCell[cellId] || []; - - // Transform posts to include comment count - const posts = cellPosts.map(post => ({ - id: post.id, - title: post.title, - content: post.content, - author: post.author, - timestamp: post.timestamp, - voteScore: post.voteScore, - commentCount: (commentsByPost[post.id] || []).length, - })); - - // Check user permissions - const isUserAdmin = currentUser - ? currentUser.address === cell.signature - : false; - const canModerate = isUserAdmin; - const canPost = currentUser - ? currentUser.verificationStatus === - EVerificationStatus.ENS_ORDINAL_VERIFIED || - currentUser.verificationStatus === - EVerificationStatus.WALLET_CONNECTED || - Boolean(currentUser.ensDetails) || - Boolean(currentUser.ordinalDetails) - : false; - - return { - ...cell, - posts, - isUserAdmin, - canModerate, - canPost, - }; - }, [cellId, cellsWithStats, postsByCell, commentsByPost, currentUser]); -} diff --git a/src/hooks/derived/useCellPosts.ts b/src/hooks/derived/useCellPosts.ts deleted file mode 100644 index 0a42234..0000000 --- a/src/hooks/derived/useCellPosts.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { useMemo } from 'react'; -import { useForumData, PostWithVoteStatus } from '@/hooks/core/useForumData'; -import { useAuth } from '@/hooks/core/useAuth'; -import { useModeration } from '@/contexts/ModerationContext'; - -export interface CellPostsOptions { - includeModerated?: boolean; - sortBy?: 'relevance' | 'timestamp' | 'votes'; - limit?: number; -} - -export interface CellPostsData { - posts: PostWithVoteStatus[]; - totalCount: number; - hasMore: boolean; - isLoading: boolean; -} - -/** - * Hook for getting posts for a specific cell with filtering and sorting - */ -export function useCellPosts( - cellId: string | undefined, - options: CellPostsOptions = {} -): CellPostsData { - const { postsByCell, isInitialLoading, cellsWithStats } = useForumData(); - const { currentUser } = useAuth(); - const { showModerated } = useModeration(); - - const { - includeModerated = showModerated, - sortBy = 'relevance', - limit, - } = options; - - return useMemo(() => { - if (!cellId) { - return { - posts: [], - totalCount: 0, - hasMore: false, - isLoading: isInitialLoading, - }; - } - - let posts = postsByCell[cellId] || []; - - // Filter moderated posts unless user is admin - if (!includeModerated) { - const cell = cellsWithStats.find(c => c.id === cellId); - const isUserAdmin = - currentUser && cell && currentUser.address === cell.signature; - - if (!isUserAdmin) { - posts = posts.filter(post => !post.moderated); - } - } - - // Sort posts - const sortedPosts = [...posts].sort((a, b) => { - switch (sortBy) { - case 'relevance': - if ( - a.relevanceScore !== undefined && - b.relevanceScore !== undefined - ) { - return b.relevanceScore - a.relevanceScore; - } - return b.timestamp - a.timestamp; - - case 'votes': - return b.voteScore - a.voteScore; - - case 'timestamp': - default: - return b.timestamp - a.timestamp; - } - }); - - // Apply limit if specified - const limitedPosts = limit ? sortedPosts.slice(0, limit) : sortedPosts; - const hasMore = limit ? sortedPosts.length > limit : false; - - return { - posts: limitedPosts, - totalCount: sortedPosts.length, - hasMore, - isLoading: isInitialLoading, - }; - }, [ - cellId, - postsByCell, - isInitialLoading, - currentUser, - cellsWithStats, - includeModerated, - sortBy, - limit, - ]); -} diff --git a/src/hooks/derived/usePost.ts b/src/hooks/derived/usePost.ts deleted file mode 100644 index 71aa9be..0000000 --- a/src/hooks/derived/usePost.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { useMemo } from 'react'; -import { - useForumData, - PostWithVoteStatus, - CommentWithVoteStatus, -} from '@/hooks/core/useForumData'; -import { useAuth } from '@/hooks/core/useAuth'; - -export interface PostData extends PostWithVoteStatus { - cell: { - id: string; - name: string; - description: string; - } | null; - comments: CommentWithVoteStatus[]; - commentCount: number; - isUserAuthor: boolean; -} - -/** - * Hook for getting a specific post with its comments and metadata - */ -export function usePost(postId: string | undefined): PostData | null { - const { postsWithVoteStatus, commentsByPost, cellsWithStats } = - useForumData(); - const { currentUser } = useAuth(); - - return useMemo(() => { - if (!postId) return null; - - const post = postsWithVoteStatus.find(p => p.id === postId); - if (!post) return null; - - const cell = cellsWithStats.find(c => c.id === post.cellId) || null; - const comments = commentsByPost[postId] || []; - const commentCount = comments.length; - const isUserAuthor = currentUser - ? currentUser.address === post.author - : false; - - return { - ...post, - cell: cell - ? { - id: cell.id, - name: cell.name, - description: cell.description, - } - : null, - comments, - commentCount, - isUserAuthor, - }; - }, [ - postId, - postsWithVoteStatus, - commentsByPost, - cellsWithStats, - currentUser, - ]); -} diff --git a/src/hooks/derived/usePostComments.ts b/src/hooks/derived/usePostComments.ts deleted file mode 100644 index 76a67bb..0000000 --- a/src/hooks/derived/usePostComments.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { useMemo } from 'react'; -import { useForumData, CommentWithVoteStatus } from '@/hooks/core/useForumData'; -import { useAuth } from '@/hooks/core/useAuth'; -import { useModeration } from '@/contexts/ModerationContext'; - -export interface PostCommentsOptions { - includeModerated?: boolean; - sortBy?: 'timestamp' | 'votes'; - limit?: number; -} - -export interface PostCommentsData { - comments: CommentWithVoteStatus[]; - totalCount: number; - hasMore: boolean; - isLoading: boolean; -} - -/** - * Hook for getting comments for a specific post with filtering and sorting - */ -export function usePostComments( - postId: string | undefined, - options: PostCommentsOptions = {} -): PostCommentsData { - const { - commentsByPost, - isInitialLoading, - postsWithVoteStatus, - cellsWithStats, - } = useForumData(); - const { currentUser } = useAuth(); - const { showModerated } = useModeration(); - - const { - includeModerated = showModerated, - sortBy = 'timestamp', - limit, - } = options; - - return useMemo(() => { - if (!postId) { - return { - comments: [], - totalCount: 0, - hasMore: false, - isLoading: isInitialLoading, - }; - } - - let comments = commentsByPost[postId] || []; - - // Filter moderated comments unless user is admin - if (!includeModerated) { - const post = postsWithVoteStatus.find(p => p.id === postId); - const cell = post ? cellsWithStats.find(c => c.id === post.cellId) : null; - const isUserAdmin = - currentUser && cell && currentUser.address === cell.signature; - - if (!isUserAdmin) { - comments = comments.filter(comment => !comment.moderated); - } - } - - // Sort comments - const sortedComments = [...comments].sort((a, b) => { - switch (sortBy) { - case 'votes': - return b.voteScore - a.voteScore; - - case 'timestamp': - default: - return a.timestamp - b.timestamp; // Oldest first for comments - } - }); - - // Apply limit if specified - const limitedComments = limit - ? sortedComments.slice(0, limit) - : sortedComments; - const hasMore = limit ? sortedComments.length > limit : false; - - return { - comments: limitedComments, - totalCount: sortedComments.length, - hasMore, - isLoading: isInitialLoading, - }; - }, [ - postId, - commentsByPost, - isInitialLoading, - currentUser, - postsWithVoteStatus, - cellsWithStats, - includeModerated, - sortBy, - limit, - ]); -} diff --git a/src/hooks/derived/useUserVotes.ts b/src/hooks/derived/useUserVotes.ts deleted file mode 100644 index 6f236b3..0000000 --- a/src/hooks/derived/useUserVotes.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { useMemo } from 'react'; -import { useForumData } from '@/hooks/core/useForumData'; -import { useAuth } from '@/hooks/core/useAuth'; - -export interface UserVoteData { - // Vote status for specific items - hasVotedOnPost: (postId: string) => boolean; - hasVotedOnComment: (commentId: string) => boolean; - getPostVoteType: (postId: string) => 'upvote' | 'downvote' | null; - getCommentVoteType: (commentId: string) => 'upvote' | 'downvote' | null; - - // User's voting history - votedPosts: Set; - votedComments: Set; - upvotedPosts: Set; - downvotedPosts: Set; - upvotedComments: Set; - downvotedComments: Set; - - // Statistics - totalVotes: number; - upvoteRatio: number; -} - -/** - * Hook for getting user's voting status and history - */ -export function useUserVotes(userAddress?: string): UserVoteData { - const { postsWithVoteStatus, commentsWithVoteStatus } = useForumData(); - const { currentUser } = useAuth(); - - const targetAddress = userAddress || currentUser?.address; - - return useMemo(() => { - if (!targetAddress) { - return { - hasVotedOnPost: () => false, - hasVotedOnComment: () => false, - getPostVoteType: () => null, - getCommentVoteType: () => null, - votedPosts: new Set(), - votedComments: new Set(), - upvotedPosts: new Set(), - downvotedPosts: new Set(), - upvotedComments: new Set(), - downvotedComments: new Set(), - totalVotes: 0, - upvoteRatio: 0, - }; - } - - // Build vote sets - const votedPosts = new Set(); - const votedComments = new Set(); - const upvotedPosts = new Set(); - const downvotedPosts = new Set(); - const upvotedComments = new Set(); - const downvotedComments = new Set(); - - // Analyze post votes - postsWithVoteStatus.forEach(post => { - const hasUpvoted = post.upvotes.some( - vote => vote.author === targetAddress - ); - const hasDownvoted = post.downvotes.some( - vote => vote.author === targetAddress - ); - - if (hasUpvoted) { - votedPosts.add(post.id); - upvotedPosts.add(post.id); - } - if (hasDownvoted) { - votedPosts.add(post.id); - downvotedPosts.add(post.id); - } - }); - - // Analyze comment votes - commentsWithVoteStatus.forEach(comment => { - const hasUpvoted = comment.upvotes.some( - vote => vote.author === targetAddress - ); - const hasDownvoted = comment.downvotes.some( - vote => vote.author === targetAddress - ); - - if (hasUpvoted) { - votedComments.add(comment.id); - upvotedComments.add(comment.id); - } - if (hasDownvoted) { - votedComments.add(comment.id); - downvotedComments.add(comment.id); - } - }); - - // Calculate statistics - const totalVotes = votedPosts.size + votedComments.size; - const totalUpvotes = upvotedPosts.size + upvotedComments.size; - const upvoteRatio = totalVotes > 0 ? totalUpvotes / totalVotes : 0; - - // Helper functions - const hasVotedOnPost = (postId: string): boolean => { - return votedPosts.has(postId); - }; - - const hasVotedOnComment = (commentId: string): boolean => { - return votedComments.has(commentId); - }; - - const getPostVoteType = (postId: string): 'upvote' | 'downvote' | null => { - if (upvotedPosts.has(postId)) return 'upvote'; - if (downvotedPosts.has(postId)) return 'downvote'; - return null; - }; - - const getCommentVoteType = ( - commentId: string - ): 'upvote' | 'downvote' | null => { - if (upvotedComments.has(commentId)) return 'upvote'; - if (downvotedComments.has(commentId)) return 'downvote'; - return null; - }; - - return { - hasVotedOnPost, - hasVotedOnComment, - getPostVoteType, - getCommentVoteType, - votedPosts, - votedComments, - upvotedPosts, - downvotedPosts, - upvotedComments, - downvotedComments, - totalVotes, - upvoteRatio, - }; - }, [postsWithVoteStatus, commentsWithVoteStatus, targetAddress]); -} diff --git a/src/hooks/index.ts b/src/hooks/index.ts deleted file mode 100644 index 39375ec..0000000 --- a/src/hooks/index.ts +++ /dev/null @@ -1,91 +0,0 @@ -// Core hooks - Main exports -export { useForumData } from './core/useForumData'; -export { useAuth } from './core/useAuth'; -export { useUserDisplay } from './core/useUserDisplay'; -export { - useBookmarks, - usePostBookmark, - useCommentBookmark, -} from './core/useBookmarks'; - -// Core types -export type { - ForumData, - CellWithStats, - PostWithVoteStatus, - CommentWithVoteStatus, -} from './core/useForumData'; - -export type { AuthState } from './core/useAuth'; -export type { - Permission, - PermissionReasons, - PermissionResult, -} from './core/usePermissions'; - -export type { UserDisplayInfo } from './core/useEnhancedUserDisplay'; - -// Derived hooks -export { useCell } from './derived/useCell'; -export type { CellData } from './derived/useCell'; - -export { usePost } from './derived/usePost'; -export type { PostData } from './derived/usePost'; - -export { useCellPosts } from './derived/useCellPosts'; -export type { CellPostsOptions, CellPostsData } from './derived/useCellPosts'; - -export { usePostComments } from './derived/usePostComments'; -export type { - PostCommentsOptions, - PostCommentsData, -} from './derived/usePostComments'; - -export { useUserVotes } from './derived/useUserVotes'; -export type { UserVoteData } from './derived/useUserVotes'; - -// Action hooks -export { useForumActions } from './actions/useForumActions'; -export type { - ForumActionStates, - ForumActions, -} from './actions/useForumActions'; - -export { useUserActions } from './actions/useUserActions'; -export type { UserActionStates, UserActions } from './actions/useUserActions'; - -export { useAuthActions } from './actions/useAuthActions'; -export type { AuthActionStates, AuthActions } from './actions/useAuthActions'; - -// Utility hooks -export { usePermissions } from './core/usePermissions'; - -export { useNetworkStatus } from './utilities/useNetworkStatus'; -export type { - NetworkHealth, - SyncStatus, - ConnectionStatus, - NetworkStatusData, -} from './utilities/useNetworkStatus'; - -export { - useWakuHealth, - useWakuReady, - useWakuHealthStatus, -} from './useWakuHealth'; -export type { WakuHealthState } from './useWakuHealth'; - -export { useForumSelectors } from './utilities/selectors'; -export type { ForumSelectors } from './utilities/selectors'; - -// Legacy hooks (for backward compatibility - will be removed) -// export { useForum } from '@/contexts/useForum'; // Use useForumData instead -// export { useAuth as useLegacyAuth } from '@/contexts/useAuth'; // Use enhanced useAuth instead - -// Re-export existing hooks that don't need changes -export { useIsMobile as useMobile } from './use-mobile'; -export { useToast } from './use-toast'; -// export { useCache } from './useCache'; // Removed - functionality moved to useForumData -export { useDelegation } from './useDelegation'; -export { useMessageSigning } from './useMessageSigning'; -export { useWallet } from './useWallet'; diff --git a/src/hooks/useDelegation.ts b/src/hooks/useDelegation.ts deleted file mode 100644 index b49de24..0000000 --- a/src/hooks/useDelegation.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { useCallback, useContext, useState, useEffect } from 'react'; -import { AuthContext } from '@/contexts/AuthContext'; -import { DelegationDuration } from '@/lib/delegation'; - -export const useDelegation = () => { - const context = useContext(AuthContext); - - if (!context) { - throw new Error('useDelegation must be used within an AuthProvider'); - } - - const { - delegateKey: contextDelegateKey, - getDelegationStatus: contextGetDelegationStatus, - clearDelegation: contextClearDelegation, - isAuthenticating, - } = context; - - const createDelegation = useCallback( - async (duration?: DelegationDuration): Promise => { - return contextDelegateKey(duration); - }, - [contextDelegateKey] - ); - - const clearDelegation = useCallback((): void => { - contextClearDelegation(); - }, [contextClearDelegation]); - - const [delegationStatus, setDelegationStatus] = useState<{ - hasDelegation: boolean; - isValid: boolean; - timeRemaining?: number; - expiresAt?: Date; - publicKey?: string; - address?: string; - walletType?: 'bitcoin' | 'ethereum'; - }>({ - hasDelegation: false, - isValid: false, - }); - - // Load delegation status - useEffect(() => { - contextGetDelegationStatus() - .then(status => { - setDelegationStatus({ - hasDelegation: status.hasDelegation, - isValid: status.isValid, - timeRemaining: status.timeRemaining, - expiresAt: status.timeRemaining - ? new Date(Date.now() + status.timeRemaining) - : undefined, - publicKey: status.publicKey, - address: status.address, - walletType: status.walletType, - }); - }) - .catch(console.error); - }, [contextGetDelegationStatus]); - - const formatTimeRemaining = useCallback((timeMs: number): string => { - const hours = Math.floor(timeMs / (1000 * 60 * 60)); - const minutes = Math.floor((timeMs % (1000 * 60 * 60)) / (1000 * 60)); - - if (hours > 24) { - const days = Math.floor(hours / 24); - return `${days} day${days === 1 ? '' : 's'}`; - } else if (hours > 0) { - return `${hours}h ${minutes}m`; - } else { - return `${minutes}m`; - } - }, []); - - return { - // Delegation status - delegationStatus, - isCreatingDelegation: isAuthenticating, - - // Delegation actions - createDelegation, - clearDelegation, - - // Utilities - formatTimeRemaining, - }; -}; diff --git a/src/hooks/useMessageSigning.ts b/src/hooks/useMessageSigning.ts deleted file mode 100644 index eb233e3..0000000 --- a/src/hooks/useMessageSigning.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { useCallback, useContext } from 'react'; -import { AuthContext } from '@/contexts/AuthContext'; -import { OpchanMessage } from '@/types/forum'; - -export const useMessageSigning = () => { - const context = useContext(AuthContext); - - if (!context) { - throw new Error('useMessageSigning must be used within an AuthProvider'); - } - - const { - signMessage: contextSignMessage, - verifyMessage: contextVerifyMessage, - getDelegationStatus, - } = context; - - const signMessage = useCallback( - async (message: OpchanMessage): Promise => { - // Check if we have a valid delegation before attempting to sign - const delegationStatus = await getDelegationStatus(); - if (!delegationStatus.isValid) { - console.warn('No valid delegation found. Cannot sign message.'); - return null; - } - - return contextSignMessage(message); - }, - [contextSignMessage, getDelegationStatus] - ); - - const verifyMessage = useCallback( - async (message: OpchanMessage): Promise => { - return await contextVerifyMessage(message); - }, - [contextVerifyMessage] - ); - - const canSignMessages = useCallback(async (): Promise => { - const delegationStatus = await getDelegationStatus(); - return delegationStatus.isValid; - }, [getDelegationStatus]); - - return { - // Message signing - signMessage, - verifyMessage, - canSignMessages, - }; -}; diff --git a/src/hooks/usePending.ts b/src/hooks/usePending.ts deleted file mode 100644 index 8c47dfe..0000000 --- a/src/hooks/usePending.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { useEffect, useState } from 'react'; -import { localDatabase } from '@/lib/database/LocalDatabase'; -import { useAuth } from '@/contexts/useAuth'; - -export function usePending(id: string | undefined) { - const [isPending, setIsPending] = useState( - id ? localDatabase.isPending(id) : false - ); - - useEffect(() => { - if (!id) return; - setIsPending(localDatabase.isPending(id)); - const unsubscribe = localDatabase.onPendingChange(() => { - setIsPending(localDatabase.isPending(id)); - }); - return unsubscribe; - }, [id]); - - return { isPending }; -} - -export function usePendingVote(targetId: string | undefined) { - const { currentUser } = useAuth(); - const [isPending, setIsPending] = useState(false); - - useEffect(() => { - const compute = () => { - if (!targetId || !currentUser?.address) return setIsPending(false); - // Find a vote authored by current user for this target that is pending - const pending = Object.values(localDatabase.cache.votes).some(v => { - return ( - v.targetId === targetId && - v.author === currentUser.address && - localDatabase.isPending(v.id) - ); - }); - setIsPending(pending); - }; - - compute(); - const unsub = localDatabase.onPendingChange(compute); - return unsub; - }, [targetId, currentUser?.address]); - - return { isPending }; -} diff --git a/src/hooks/useWakuHealth.ts b/src/hooks/useWakuHealth.ts deleted file mode 100644 index e763c99..0000000 --- a/src/hooks/useWakuHealth.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { useState, useEffect, useCallback } from 'react'; -import { HealthStatus } from '@waku/sdk'; -import messageManager from '@/lib/waku'; - -export interface WakuHealthState { - isReady: boolean; - health: HealthStatus; - isInitialized: boolean; - connectionStatus: 'connecting' | 'connected' | 'disconnected' | 'error'; -} - -/** - * Hook for monitoring Waku network health and connection status - * Provides real-time updates on network state and health - */ -export function useWakuHealth(): WakuHealthState { - const [isReady, setIsReady] = useState(false); - const [health, setHealth] = useState(HealthStatus.Unhealthy); - const [isInitialized, setIsInitialized] = useState(false); - const [connectionStatus, setConnectionStatus] = useState< - 'connecting' | 'connected' | 'disconnected' | 'error' - >('connecting'); - - const updateHealth = useCallback( - (ready: boolean, currentHealth: HealthStatus) => { - setIsReady(ready); - setHealth(currentHealth); - - // Update connection status based on health - if (ready) { - setConnectionStatus('connected'); - } else if (currentHealth === HealthStatus.Unhealthy) { - setConnectionStatus('disconnected'); - } else { - setConnectionStatus('connecting'); - } - }, - [] - ); - - useEffect(() => { - // Check if messageManager is initialized - try { - const currentHealth = messageManager.currentHealth; - const currentReady = messageManager.isReady; - - setIsInitialized(true); - updateHealth(currentReady, currentHealth); - - // Subscribe to health changes - const unsubscribe = messageManager.onHealthChange(updateHealth); - - return unsubscribe; - } catch (error) { - console.error('Failed to initialize Waku health monitoring:', error); - setConnectionStatus('error'); - setIsInitialized(false); - return undefined; - } - }, [updateHealth]); - - return { - isReady, - health, - isInitialized, - connectionStatus, - }; -} - -/** - * Hook that provides a simple boolean indicating if Waku is ready for use - * Useful for conditional rendering and loading states - */ -export function useWakuReady(): boolean { - const { isReady } = useWakuHealth(); - return isReady; -} - -/** - * Hook that provides health status with human-readable descriptions - */ -export function useWakuHealthStatus() { - const { isReady, health, connectionStatus } = useWakuHealth(); - - const getHealthDescription = useCallback(() => { - switch (health) { - case HealthStatus.SufficientlyHealthy: - return 'Network is healthy and fully operational'; - case HealthStatus.MinimallyHealthy: - return 'Network is minimally healthy and functional'; - case HealthStatus.Unhealthy: - return 'Network is unhealthy or disconnected'; - default: - return 'Network status unknown'; - } - }, [health]); - - const getStatusColor = useCallback(() => { - if (isReady) return 'green'; - if (health === HealthStatus.Unhealthy) return 'red'; - return 'yellow'; - }, [isReady, health]); - - const getStatusMessage = useCallback(() => { - switch (connectionStatus) { - case 'connecting': - return 'Connecting to Waku network...'; - case 'connected': - return 'Connected to Waku network'; - case 'disconnected': - return 'Disconnected from Waku network'; - case 'error': - return 'Error connecting to Waku network'; - default: - return 'Unknown connection status'; - } - }, [connectionStatus]); - - return { - isReady, - health, - connectionStatus, - description: getHealthDescription(), - statusColor: getStatusColor(), - statusMessage: getStatusMessage(), - }; -} diff --git a/src/hooks/useWallet.ts b/src/hooks/useWallet.ts deleted file mode 100644 index ff28f6d..0000000 --- a/src/hooks/useWallet.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { useCallback, useContext } from 'react'; -import { AuthContext } from '@/contexts/AuthContext'; -import { modal } from '@reown/appkit/react'; - -export const useWallet = () => { - const context = useContext(AuthContext); - - if (!context) { - throw new Error('useWallet must be used within an AuthProvider'); - } - - const { - currentUser, - isAuthenticated, - verificationStatus, - connectWallet: contextConnectWallet, - disconnectWallet: contextDisconnectWallet, - } = context; - - const connect = useCallback(async (): Promise => { - return contextConnectWallet(); - }, [contextConnectWallet]); - - const disconnect = useCallback((): void => { - contextDisconnectWallet(); - }, [contextDisconnectWallet]); - - const openModal = useCallback(async (): Promise => { - if (modal) { - await modal.open(); - } - }, []); - - const closeModal = useCallback((): void => { - if (modal) { - modal.close(); - } - }, []); - - return { - // Wallet state - isConnected: isAuthenticated, - address: currentUser?.address, - walletType: currentUser?.walletType, - verificationStatus, - currentUser, - - // Wallet actions - connect, - disconnect, - openModal, - closeModal, - }; -}; diff --git a/src/hooks/utilities/selectors.ts b/src/hooks/utilities/selectors.ts deleted file mode 100644 index 9926553..0000000 --- a/src/hooks/utilities/selectors.ts +++ /dev/null @@ -1,339 +0,0 @@ -import { useMemo } from 'react'; -import { ForumData } from '@/hooks/core/useForumData'; -import { Cell, Post, Comment } from '@/types/forum'; -import { EVerificationStatus } from '@/types/identity'; - -// Selector types for different data slices -export type CellSelector = (cells: Cell[]) => T; -export type PostSelector = (posts: Post[]) => T; -export type CommentSelector = (comments: Comment[]) => T; - -// Common selector patterns -export interface ForumSelectors { - // Cell selectors - selectCellsByActivity: () => Cell[]; - selectCellsByMemberCount: () => Cell[]; - selectCellsByRelevance: () => Cell[]; - selectCellById: (id: string) => Cell | null; - selectCellsByOwner: (ownerAddress: string) => Cell[]; - - // Post selectors - selectPostsByCell: (cellId: string) => Post[]; - selectPostsByAuthor: (authorAddress: string) => Post[]; - selectPostsByVoteScore: (minScore?: number) => Post[]; - selectTrendingPosts: (timeframe?: number) => Post[]; - selectRecentPosts: (limit?: number) => Post[]; - selectPostById: (id: string) => Post | null; - - // Comment selectors - selectCommentsByPost: (postId: string) => Comment[]; - selectCommentsByAuthor: (authorAddress: string) => Comment[]; - selectRecentComments: (limit?: number) => Comment[]; - selectCommentById: (id: string) => Comment | null; - - // User-specific selectors - selectUserPosts: (userAddress: string) => Post[]; - selectUserComments: (userAddress: string) => Comment[]; - selectUserActivity: (userAddress: string) => { - posts: Post[]; - comments: Comment[]; - }; - selectVerifiedUsers: () => string[]; - selectActiveUsers: (timeframe?: number) => string[]; - - // Search and filter selectors - searchPosts: (query: string) => Post[]; - searchComments: (query: string) => Comment[]; - searchCells: (query: string) => Cell[]; - filterByVerification: ( - items: (Post | Comment)[], - level: EVerificationStatus - ) => (Post | Comment)[]; - - // Aggregation selectors - selectStats: () => { - totalCells: number; - totalPosts: number; - totalComments: number; - totalUsers: number; - verifiedUsers: number; - }; -} - -/** - * Hook providing optimized selectors for forum data - */ -export function useForumSelectors(forumData: ForumData): ForumSelectors { - const { - cells, - postsWithVoteStatus: posts, - commentsWithVoteStatus: comments, - userVerificationStatus, - } = forumData; - - // Cell selectors - const selectCellsByActivity = useMemo(() => { - return (): Cell[] => { - return [...cells].sort((a, b) => { - const aActivity = - 'recentActivity' in b ? (b.recentActivity as number) : 0; - const bActivity = - 'recentActivity' in a ? (a.recentActivity as number) : 0; - return aActivity - bActivity; - }); - }; - }, [cells]); - - const selectCellsByMemberCount = useMemo(() => { - return (): Cell[] => { - return [...cells].sort( - (a, b) => (b.activeMemberCount || 0) - (a.activeMemberCount || 0) - ); - }; - }, [cells]); - - const selectCellsByRelevance = useMemo(() => { - return (): Cell[] => { - return [...cells].sort( - (a, b) => (b.relevanceScore || 0) - (a.relevanceScore || 0) - ); - }; - }, [cells]); - - const selectCellById = useMemo(() => { - return (id: string): Cell | null => { - return cells.find(cell => cell.id === id) || null; - }; - }, [cells]); - - const selectCellsByOwner = useMemo(() => { - return (ownerAddress: string): Cell[] => { - return cells.filter(cell => cell.signature === ownerAddress); - }; - }, [cells]); - - // Post selectors - const selectPostsByCell = useMemo(() => { - return (cellId: string): Post[] => { - return posts.filter(post => post.cellId === cellId); - }; - }, [posts]); - - const selectPostsByAuthor = useMemo(() => { - return (authorAddress: string): Post[] => { - return posts.filter(post => post.author === authorAddress); - }; - }, [posts]); - - const selectPostsByVoteScore = useMemo(() => { - return (minScore: number = 0): Post[] => { - return posts.filter(post => post.voteScore >= minScore); - }; - }, [posts]); - - const selectTrendingPosts = useMemo(() => { - return (timeframe: number = 7 * 24 * 60 * 60 * 1000): Post[] => { - // 7 days default - const cutoff = Date.now() - timeframe; - return posts - .filter(post => post.timestamp > cutoff) - .sort((a, b) => { - // Sort by relevance score if available, otherwise by vote score - if ( - a.relevanceScore !== undefined && - b.relevanceScore !== undefined - ) { - return b.relevanceScore - a.relevanceScore; - } - return b.voteScore - a.voteScore; - }); - }; - }, [posts]); - - const selectRecentPosts = useMemo(() => { - return (limit: number = 10): Post[] => { - return [...posts] - .sort((a, b) => b.timestamp - a.timestamp) - .slice(0, limit); - }; - }, [posts]); - - const selectPostById = useMemo(() => { - return (id: string): Post | null => { - return posts.find(post => post.id === id) || null; - }; - }, [posts]); - - // Comment selectors - const selectCommentsByPost = useMemo(() => { - return (postId: string): Comment[] => { - return comments.filter(comment => comment.postId === postId); - }; - }, [comments]); - - const selectCommentsByAuthor = useMemo(() => { - return (authorAddress: string): Comment[] => { - return comments.filter(comment => comment.author === authorAddress); - }; - }, [comments]); - - const selectRecentComments = useMemo(() => { - return (limit: number = 10): Comment[] => { - return [...comments] - .sort((a, b) => b.timestamp - a.timestamp) - .slice(0, limit); - }; - }, [comments]); - - const selectCommentById = useMemo(() => { - return (id: string): Comment | null => { - return comments.find(comment => comment.id === id) || null; - }; - }, [comments]); - - // User-specific selectors - const selectUserPosts = useMemo(() => { - return (userAddress: string): Post[] => { - return posts.filter(post => post.author === userAddress); - }; - }, [posts]); - - const selectUserComments = useMemo(() => { - return (userAddress: string): Comment[] => { - return comments.filter(comment => comment.author === userAddress); - }; - }, [comments]); - - const selectUserActivity = useMemo(() => { - return (userAddress: string) => { - return { - posts: posts.filter(post => post.author === userAddress), - comments: comments.filter(comment => comment.author === userAddress), - }; - }; - }, [posts, comments]); - - const selectVerifiedUsers = useMemo(() => { - return (): string[] => { - return Object.entries(userVerificationStatus) - .filter(([_, status]) => status.isVerified) - .map(([address]) => address); - }; - }, [userVerificationStatus]); - - const selectActiveUsers = useMemo(() => { - return (timeframe: number = 7 * 24 * 60 * 60 * 1000): string[] => { - // 7 days default - const cutoff = Date.now() - timeframe; - const activeUsers = new Set(); - - posts.forEach(post => { - if (post.timestamp > cutoff) { - activeUsers.add(post.author); - } - }); - - comments.forEach(comment => { - if (comment.timestamp > cutoff) { - activeUsers.add(comment.author); - } - }); - - return Array.from(activeUsers); - }; - }, [posts, comments]); - - // Search selectors - const searchPosts = useMemo(() => { - return (query: string): Post[] => { - const lowerQuery = query.toLowerCase(); - return posts.filter( - post => - post.title.toLowerCase().includes(lowerQuery) || - post.content.toLowerCase().includes(lowerQuery) - ); - }; - }, [posts]); - - const searchComments = useMemo(() => { - return (query: string): Comment[] => { - const lowerQuery = query.toLowerCase(); - return comments.filter(comment => - comment.content.toLowerCase().includes(lowerQuery) - ); - }; - }, [comments]); - - const searchCells = useMemo(() => { - return (query: string): Cell[] => { - const lowerQuery = query.toLowerCase(); - return cells.filter( - cell => - cell.name.toLowerCase().includes(lowerQuery) || - cell.description.toLowerCase().includes(lowerQuery) - ); - }; - }, [cells]); - - const filterByVerification = useMemo(() => { - return ( - items: (Post | Comment)[], - level: EVerificationStatus - ): (Post | Comment)[] => { - return items.filter(item => { - const userStatus = userVerificationStatus[item.author]; - return userStatus?.verificationStatus === level; - }); - }; - }, [userVerificationStatus]); - - // Aggregation selectors - const selectStats = useMemo(() => { - return () => { - const uniqueUsers = new Set([ - ...posts.map(post => post.author), - ...comments.map(comment => comment.author), - ]); - - const verifiedUsers = Object.values(userVerificationStatus).filter( - status => status.isVerified - ).length; - - return { - totalCells: cells.length, - totalPosts: posts.length, - totalComments: comments.length, - totalUsers: uniqueUsers.size, - verifiedUsers, - }; - }; - }, [cells, posts, comments, userVerificationStatus]); - - return { - selectCellsByActivity, - selectCellsByMemberCount, - selectCellsByRelevance, - selectCellById, - selectCellsByOwner, - selectPostsByCell, - selectPostsByAuthor, - selectPostsByVoteScore, - selectTrendingPosts, - selectRecentPosts, - selectPostById, - selectCommentsByPost, - selectCommentsByAuthor, - selectRecentComments, - selectCommentById, - selectUserPosts, - selectUserComments, - selectUserActivity, - selectVerifiedUsers, - selectActiveUsers, - searchPosts, - searchComments, - searchCells, - filterByVerification, - selectStats, - }; -} diff --git a/src/hooks/utilities/useNetworkStatus.ts b/src/hooks/utilities/useNetworkStatus.ts deleted file mode 100644 index 59c4176..0000000 --- a/src/hooks/utilities/useNetworkStatus.ts +++ /dev/null @@ -1,241 +0,0 @@ -import { useMemo, useState, useEffect } from 'react'; -import { useForum } from '@/contexts/useForum'; -import { useAuth } from '@/hooks/core/useAuth'; -import { useAuth as useAuthContext } from '@/contexts/useAuth'; -import { DelegationFullStatus } from '@/lib/delegation'; - -export interface NetworkHealth { - isConnected: boolean; - isHealthy: boolean; - lastSync: number | null; - syncAge: string | null; - issues: string[]; -} - -export interface SyncStatus { - isInitialLoading: boolean; - isRefreshing: boolean; - isSyncing: boolean; - lastRefresh: number | null; - nextRefresh: number | null; - autoRefreshEnabled: boolean; -} - -export interface ConnectionStatus { - waku: { - connected: boolean; - peers: number; - status: 'connected' | 'connecting' | 'disconnected' | 'error'; - }; - wallet: { - connected: boolean; - network: string | null; - status: 'connected' | 'connecting' | 'disconnected' | 'error'; - }; - delegation: { - active: boolean; - expires: number | null; - status: 'active' | 'expired' | 'none'; - }; -} - -export interface NetworkStatusData { - // Overall status - health: NetworkHealth; - sync: SyncStatus; - connections: ConnectionStatus; - - // Actions - canRefresh: boolean; - canSync: boolean; - needsAttention: boolean; - - // Helper methods - getStatusMessage: () => string; - getHealthColor: () => 'green' | 'yellow' | 'red'; - getRecommendedActions: () => string[]; -} - -/** - * Hook for monitoring network status and connection health - */ -export function useNetworkStatus(): NetworkStatusData { - const { isNetworkConnected, isInitialLoading, isRefreshing, error } = - useForum(); - - const { isAuthenticated, currentUser } = useAuth(); - const { getDelegationStatus } = useAuthContext(); - const [delegationInfo, setDelegationInfo] = - useState(null); - - // Load delegation status - useEffect(() => { - getDelegationStatus().then(setDelegationInfo).catch(console.error); - }, [getDelegationStatus]); - - // Network health assessment - const health = useMemo((): NetworkHealth => { - const issues: string[] = []; - - if (!isNetworkConnected) { - issues.push('Waku network disconnected'); - } - - if (error) { - issues.push(`Forum error: ${error}`); - } - - if (isAuthenticated && !delegationInfo?.isValid) { - issues.push('Key delegation expired'); - } - - const isHealthy = issues.length === 0; - const lastSync = Date.now(); // This would come from actual sync tracking - const syncAge = lastSync ? formatTimeAgo(lastSync) : null; - - return { - isConnected: isNetworkConnected, - isHealthy, - lastSync, - syncAge, - issues, - }; - }, [isNetworkConnected, error, isAuthenticated, delegationInfo?.isValid]); - - // Sync status - const sync = useMemo((): SyncStatus => { - const lastRefresh = Date.now() - 30000; // Mock: 30 seconds ago - const nextRefresh = lastRefresh + 60000; // Mock: every minute - - return { - isInitialLoading, - isRefreshing, - isSyncing: isInitialLoading || isRefreshing, - lastRefresh, - nextRefresh, - autoRefreshEnabled: true, // This would be configurable - }; - }, [isInitialLoading, isRefreshing]); - - // Connection status - const connections = useMemo((): ConnectionStatus => { - return { - waku: { - connected: isNetworkConnected, - peers: isNetworkConnected ? 3 : 0, // Mock peer count - status: isNetworkConnected ? 'connected' : 'disconnected', - }, - wallet: { - connected: isAuthenticated, - network: currentUser?.walletType === 'bitcoin' ? 'Bitcoin' : 'Ethereum', - status: isAuthenticated ? 'connected' : 'disconnected', - }, - delegation: { - active: delegationInfo?.isValid || false, - expires: delegationInfo?.timeRemaining || null, - status: delegationInfo?.isValid ? 'active' : 'expired', - }, - }; - }, [isNetworkConnected, isAuthenticated, currentUser, delegationInfo]); - - // Status assessment - const canRefresh = !isRefreshing && !isInitialLoading; - const canSync = isNetworkConnected && !isRefreshing; - const needsAttention = !health.isHealthy || !delegationInfo?.isValid; - - // Helper methods - const getStatusMessage = useMemo(() => { - return (): string => { - if (isInitialLoading) return 'Loading forum data...'; - if (isRefreshing) return 'Refreshing data...'; - if (!isNetworkConnected) return 'Network disconnected'; - if (error) return `Error: ${error}`; - if (health.issues.length > 0) return health.issues[0] || 'Unknown issue'; - return 'All systems operational'; - }; - }, [ - isInitialLoading, - isRefreshing, - isNetworkConnected, - error, - health.issues, - ]); - - const getHealthColor = useMemo(() => { - return (): 'green' | 'yellow' | 'red' => { - if (!isNetworkConnected || error) return 'red'; - if (health.issues.length > 0 || !delegationInfo?.isValid) return 'yellow'; - return 'green'; - }; - }, [ - isNetworkConnected, - error, - health.issues.length, - delegationInfo?.isValid, - ]); - - const getRecommendedActions = useMemo(() => { - return (): string[] => { - const actions: string[] = []; - - if (!isNetworkConnected) { - actions.push('Check your internet connection'); - actions.push('Try refreshing the page'); - } - - if (!isAuthenticated) { - actions.push('Connect your wallet'); - } - - if (!delegationInfo?.isValid) { - actions.push('Renew key delegation'); - } - - if ( - delegationInfo?.isValid && - delegationInfo?.timeRemaining && - delegationInfo.timeRemaining < 3600 - ) { - actions.push('Consider renewing key delegation soon'); - } - - if (error) { - actions.push('Try refreshing forum data'); - } - - if (actions.length === 0) { - actions.push('All systems are working normally'); - } - - return actions; - }; - }, [isNetworkConnected, isAuthenticated, delegationInfo, error]); - - return { - health, - sync, - connections, - canRefresh, - canSync, - needsAttention, - getStatusMessage, - getHealthColor, - getRecommendedActions, - }; -} - -// Helper function to format time ago -function formatTimeAgo(timestamp: number): string { - const now = Date.now(); - const diff = now - timestamp; - - const seconds = Math.floor(diff / 1000); - const minutes = Math.floor(seconds / 60); - const hours = Math.floor(minutes / 60); - const days = Math.floor(hours / 24); - - if (days > 0) return `${days}d ago`; - if (hours > 0) return `${hours}h ago`; - if (minutes > 0) return `${minutes}m ago`; - return `${seconds}s ago`; -} diff --git a/src/lib/services/UserIdentityService.ts b/src/lib/services/UserIdentityService.ts deleted file mode 100644 index 26619e6..0000000 --- a/src/lib/services/UserIdentityService.ts +++ /dev/null @@ -1,486 +0,0 @@ -import { EVerificationStatus, EDisplayPreference } from '@/types/identity'; -import { - UnsignedUserProfileUpdateMessage, - UserProfileUpdateMessage, - MessageType, - UserIdentityCache, -} from '@/types/waku'; -import { MessageService } from './MessageService'; -import messageManager from '@/lib/waku'; -import { localDatabase } from '@/lib/database/LocalDatabase'; -import { WalletManager } from '@/lib/wallet'; - -export interface UserIdentity { - address: string; - ensName?: string; - ordinalDetails?: { - ordinalId: string; - ordinalDetails: string; - }; - callSign?: string; - displayPreference: EDisplayPreference; - lastUpdated: number; - verificationStatus: EVerificationStatus; -} - -export class UserIdentityService { - private messageService: MessageService; - private userIdentityCache: UserIdentityCache = {}; - private refreshListeners: Set<(address: string) => void> = new Set(); - - constructor(messageService: MessageService) { - this.messageService = messageService; - } - - /** - * Get user identity from cache or resolve from sources - */ - async getUserIdentity(address: string): Promise { - // Check internal cache first - if (this.userIdentityCache[address]) { - const cached = this.userIdentityCache[address]; - if (import.meta.env?.DEV) { - console.debug('UserIdentityService: cache hit (internal)'); - } - // Enrich with ENS name if missing and ETH address - if (!cached.ensName && address.startsWith('0x')) { - const ensName = await this.resolveENSName(address); - if (ensName) { - cached.ensName = ensName; - } - } - return { - address, - ensName: cached.ensName, - ordinalDetails: cached.ordinalDetails, - callSign: cached.callSign, - displayPreference: cached.displayPreference, - lastUpdated: cached.lastUpdated, - verificationStatus: this.mapVerificationStatus( - cached.verificationStatus - ), - }; - } - - // Check LocalDatabase first for persisted identities (warm start) - const persisted = localDatabase.cache.userIdentities[address]; - if (persisted) { - this.userIdentityCache[address] = { - ensName: persisted.ensName, - ordinalDetails: persisted.ordinalDetails, - callSign: persisted.callSign, - displayPreference: persisted.displayPreference, - lastUpdated: persisted.lastUpdated, - verificationStatus: persisted.verificationStatus, - }; - const result = { - address, - ensName: persisted.ensName, - ordinalDetails: persisted.ordinalDetails, - callSign: persisted.callSign, - displayPreference: persisted.displayPreference, - lastUpdated: persisted.lastUpdated, - verificationStatus: this.mapVerificationStatus( - persisted.verificationStatus - ), - } as UserIdentity; - // Enrich with ENS name if missing and ETH address - if (!result.ensName && address.startsWith('0x')) { - const ensName = await this.resolveENSName(address); - if (ensName) { - result.ensName = ensName; - this.userIdentityCache[address].ensName = ensName; - } - } - return result; - } - - // Fallback: Check Waku message cache - const cacheServiceData = - messageManager.messageCache.userIdentities[address]; - - if (cacheServiceData) { - if (import.meta.env?.DEV) { - console.debug('UserIdentityService: cache hit (message cache)'); - } - - // Store in internal cache for future use - this.userIdentityCache[address] = { - ensName: cacheServiceData.ensName, - ordinalDetails: cacheServiceData.ordinalDetails, - callSign: cacheServiceData.callSign, - displayPreference: cacheServiceData.displayPreference, - lastUpdated: cacheServiceData.lastUpdated, - verificationStatus: cacheServiceData.verificationStatus, - }; - - const result = { - address, - ensName: cacheServiceData.ensName, - ordinalDetails: cacheServiceData.ordinalDetails, - callSign: cacheServiceData.callSign, - displayPreference: cacheServiceData.displayPreference, - lastUpdated: cacheServiceData.lastUpdated, - verificationStatus: this.mapVerificationStatus( - cacheServiceData.verificationStatus - ), - } as UserIdentity; - // Enrich with ENS name if missing and ETH address - if (!result.ensName && address.startsWith('0x')) { - const ensName = await this.resolveENSName(address); - if (ensName) { - result.ensName = ensName; - this.userIdentityCache[address].ensName = ensName; - } - } - return result; - } - - if (import.meta.env?.DEV) { - console.debug('UserIdentityService: cache miss, resolving'); - } - - // Try to resolve identity from various sources - const identity = await this.resolveUserIdentity(address); - if (identity) { - this.userIdentityCache[address] = { - ensName: identity.ensName, - ordinalDetails: identity.ordinalDetails, - callSign: identity.callSign, - displayPreference: identity.displayPreference, - lastUpdated: identity.lastUpdated, - verificationStatus: identity.verificationStatus, - }; - } - - return identity; - } - - /** - * Force a fresh identity resolution bypassing caches and LocalDatabase. - * Useful for explicit verification flows where we must hit upstream resolvers. - */ - async getUserIdentityFresh(address: string): Promise { - if (import.meta.env?.DEV) { - console.debug('UserIdentityService: fresh resolve requested'); - } - const identity = await this.resolveUserIdentity(address); - if (identity) { - // Update in-memory cache to reflect the fresh result - this.userIdentityCache[address] = { - ensName: identity.ensName, - ordinalDetails: identity.ordinalDetails, - callSign: identity.callSign, - displayPreference: identity.displayPreference, - lastUpdated: identity.lastUpdated, - verificationStatus: identity.verificationStatus, - }; - } - return identity; - } - - /** - * Get all cached user identities - */ - getAllUserIdentities(): UserIdentity[] { - return Object.entries(this.userIdentityCache).map(([address, cached]) => ({ - address, - ensName: cached.ensName, - ordinalDetails: cached.ordinalDetails, - callSign: cached.callSign, - displayPreference: cached.displayPreference, - lastUpdated: cached.lastUpdated, - verificationStatus: this.mapVerificationStatus(cached.verificationStatus), - })); - } - - /** - * Update user profile via Waku message - */ - async updateUserProfile( - address: string, - callSign: string | undefined, - displayPreference: EDisplayPreference - ): Promise { - try { - if (import.meta.env?.DEV) { - console.debug('UserIdentityService: updating profile', { address }); - } - - const timestamp = Date.now(); - const unsignedMessage: UnsignedUserProfileUpdateMessage = { - id: crypto.randomUUID(), - type: MessageType.USER_PROFILE_UPDATE, - timestamp, - author: address, - displayPreference, - }; - // Only include callSign if provided and non-empty - if (callSign && callSign.trim()) { - unsignedMessage.callSign = callSign.trim(); - } - - if (import.meta.env?.DEV) { - console.debug('UserIdentityService: created unsigned message'); - } - - const signedMessage = - await this.messageService.signAndBroadcastMessage(unsignedMessage); - - if (import.meta.env?.DEV) { - console.debug( - 'UserIdentityService: message broadcast result', - !!signedMessage - ); - } - - // If broadcast was successful, immediately update local cache - if (signedMessage) { - this.updateUserIdentityFromMessage( - signedMessage as UserProfileUpdateMessage - ); - - // Also update the local database cache immediately - if (this.userIdentityCache[address]) { - const updatedIdentity = { - ...this.userIdentityCache[address], - callSign: - callSign && callSign.trim() - ? callSign.trim() - : this.userIdentityCache[address].callSign, - displayPreference, - lastUpdated: timestamp, - }; - - localDatabase.cache.userIdentities[address] = updatedIdentity; - - // Persist to IndexedDB using the storeMessage method - const profileMessage: UserProfileUpdateMessage = { - id: unsignedMessage.id, - type: MessageType.USER_PROFILE_UPDATE, - timestamp, - author: address, - displayPreference, - signature: signedMessage.signature, - browserPubKey: signedMessage.browserPubKey, - delegationProof: signedMessage.delegationProof, - }; - if (callSign && callSign.trim()) { - profileMessage.callSign = callSign.trim(); - } - - // Apply the message to update the database - await localDatabase.applyMessage(profileMessage); - - // Notify listeners that the user identity has been updated - this.notifyRefreshListeners(address); - } - } - - return !!signedMessage; - } catch (error) { - console.error('Failed to update user profile:', error); - return false; - } - } - - /** - * Resolve user identity from various sources - */ - private async resolveUserIdentity( - address: string - ): Promise { - try { - const [ensName, ordinalDetails] = await Promise.all([ - this.resolveENSName(address), - this.resolveOrdinalDetails(address), - ]); - - // Default to wallet address display preference - const defaultDisplayPreference: EDisplayPreference = - EDisplayPreference.WALLET_ADDRESS; - - // Default verification status based on what we can resolve - let verificationStatus: EVerificationStatus = - EVerificationStatus.WALLET_UNCONNECTED; - if (ensName || ordinalDetails) { - verificationStatus = EVerificationStatus.ENS_ORDINAL_VERIFIED; - } - - return { - address, - ensName: ensName || undefined, - ordinalDetails: ordinalDetails || undefined, - callSign: undefined, // Will be populated from Waku messages - displayPreference: defaultDisplayPreference, - lastUpdated: Date.now(), - verificationStatus, - }; - } catch (error) { - console.error('Failed to resolve user identity:', error); - return null; - } - } - - /** - * Resolve ENS name from Ethereum address - */ - private async resolveENSName(address: string): Promise { - if (!address.startsWith('0x')) { - return null; // Not an Ethereum address - } - - try { - // Import the ENS resolver from wagmi - const { getEnsName } = await import('@wagmi/core'); - const { config } = await import('@/lib/wallet/config'); - - const ensName = await getEnsName(config, { - address: address as `0x${string}`, - }); - - return ensName || null; - } catch (error) { - console.error('Failed to resolve ENS name:', error); - return null; - } - } - - /** - * Resolve Ordinal details from Bitcoin address - */ - private async resolveOrdinalDetails( - address: string - ): Promise<{ ordinalId: string; ordinalDetails: string } | null> { - try { - if (address.startsWith('0x')) { - return null; - } - - const inscriptions = await WalletManager.resolveOperatorOrdinals(address); - if (Array.isArray(inscriptions) && inscriptions.length > 0) { - const first = inscriptions[0]!; - return { - ordinalId: first.inscription_id, - ordinalDetails: - first.parent_inscription_id || 'Operator badge present', - }; - } - return null; - } catch (error) { - console.error('Failed to resolve Ordinal details:', error); - return null; - } - } - - /** - * Update user identity from Waku message - */ - updateUserIdentityFromMessage(message: UserProfileUpdateMessage): void { - const { author, callSign, displayPreference, timestamp } = message; - - if (!this.userIdentityCache[author]) { - // Create new identity entry if it doesn't exist - this.userIdentityCache[author] = { - ensName: undefined, - ordinalDetails: undefined, - callSign: undefined, - displayPreference, - lastUpdated: timestamp, - verificationStatus: EVerificationStatus.WALLET_UNCONNECTED, - }; - } - - // Update only if this message is newer - if (timestamp > this.userIdentityCache[author].lastUpdated) { - this.userIdentityCache[author] = { - ...this.userIdentityCache[author], - callSign, - displayPreference, - lastUpdated: timestamp, - }; - - // Notify listeners that the user identity has been updated - this.notifyRefreshListeners(author); - } - } - - /** - * Map verification status string to enum - */ - private mapVerificationStatus(status: string): EVerificationStatus { - switch (status) { - // Legacy message-cache statuses - case 'verified-basic': - return EVerificationStatus.WALLET_CONNECTED; - case 'verified-owner': - return EVerificationStatus.ENS_ORDINAL_VERIFIED; - case 'verifying': - return EVerificationStatus.WALLET_CONNECTED; // Temporary state during verification - - // Enum string values persisted in LocalDatabase - case EVerificationStatus.WALLET_UNCONNECTED: - return EVerificationStatus.WALLET_UNCONNECTED; - case EVerificationStatus.WALLET_CONNECTED: - return EVerificationStatus.WALLET_CONNECTED; - case EVerificationStatus.ENS_ORDINAL_VERIFIED: - return EVerificationStatus.ENS_ORDINAL_VERIFIED; - - default: - return EVerificationStatus.WALLET_UNCONNECTED; - } - } - - /** - * Refresh user identity (force re-resolution) - */ - async refreshUserIdentity(address: string): Promise { - delete this.userIdentityCache[address]; - await this.getUserIdentity(address); - } - - /** - * Clear user identity cache - */ - clearUserIdentityCache(): void { - this.userIdentityCache = {}; - } - - /** - * Add a refresh listener for when user identity data changes - */ - addRefreshListener(listener: (address: string) => void): () => void { - this.refreshListeners.add(listener); - return () => this.refreshListeners.delete(listener); - } - - /** - * Notify all listeners that user identity data has changed - */ - private notifyRefreshListeners(address: string): void { - this.refreshListeners.forEach(listener => listener(address)); - } - - /** - * Get display name for user based on their preferences - */ - getDisplayName(address: string): string { - const identity = this.userIdentityCache[address]; - if (!identity) { - return `${address.slice(0, 6)}...${address.slice(-4)}`; - } - - if ( - identity.displayPreference === EDisplayPreference.CALL_SIGN && - identity.callSign - ) { - return identity.callSign; - } - - if (identity.ensName) { - return identity.ensName; - } - - return `${address.slice(0, 6)}...${address.slice(-4)}`; - } -} diff --git a/src/lib/waku/constants.ts b/src/lib/waku/constants.ts deleted file mode 100644 index f6441bb..0000000 --- a/src/lib/waku/constants.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Single content topic for all message types - * Different message types are parsed from the message content itself - */ -export const CONTENT_TOPIC = '/opchan-demo/1/messages/proto'; - -/** - * Bootstrap nodes for the Waku network - * These are public Waku nodes that our node will connect to on startup - */ -export const BOOTSTRAP_NODES = { - '42': [ - '/dns4/waku-test.bloxy.one/tcp/8095/wss/p2p/16Uiu2HAmSZbDB7CusdRhgkD81VssRjQV5ZH13FbzCGcdnbbh6VwZ', - '/dns4/node-01.do-ams3.waku.sandbox.status.im/tcp/30303/p2p/16Uiu2HAmNaeL4p3WEYzC9mgXBmBWSgWjPHRvatZTXnp8Jgv3iKsb', - '/dns4/vps-aaa00d52.vps.ovh.ca/tcp/8000/wss/p2p/16Uiu2HAm9PftGgHZwWE3wzdMde4m3kT2eYJFXLZfGoSED3gysofk', - ], -}; diff --git a/src/main.tsx b/src/main.tsx deleted file mode 100644 index 4f0de65..0000000 --- a/src/main.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { createRoot } from 'react-dom/client'; -import App from './App.tsx'; -import './index.css'; -import { Buffer } from 'buffer'; - -// Ensure Buffer is available in the browser (needed by some wallet libs) -if (!(window as Window & typeof globalThis).Buffer) { - (window as Window & typeof globalThis).Buffer = Buffer; -} - -createRoot(document.getElementById('root')!).render(); diff --git a/tsconfig.json b/tsconfig.json index 0286778..bd62966 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,35 +1,24 @@ { - "files": [], - "references": [ - { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" } - ], "compilerOptions": { - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - }, - /* Enhanced Type Checking */ - "strict": true, - "noImplicitAny": true, - "noUnusedParameters": true, - "noUnusedLocals": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "strictBindCallApply": true, - "strictPropertyInitialization": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "noImplicitOverride": true, - "allowUnusedLabels": false, - "allowUnreachableCode": false, - - /* Module Resolution */ - "skipLibCheck": true, - "allowJs": true, - "forceConsistentCasingInFileNames": true, + "target": "ES2020", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "node", + "esModuleInterop": true, "allowSyntheticDefaultImports": true, - "esModuleInterop": true - } -} + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist", + "jsx": "react-jsx", + "removeComments": true, + "noEmitOnError": true, + "incremental": true, + "tsBuildInfoFile": ".tsbuildinfo" + }, + "include": ["packages/*/src/**/*", "app/utils/urlLoads.test.ts", "app/utils/urlLoads.ts"], + "exclude": ["node_modules", "**/dist", "**/*.test.ts", "**/*.spec.ts"] +} \ No newline at end of file diff --git a/vercel.json b/vercel.json index 0f32683..27f7ef1 100644 --- a/vercel.json +++ b/vercel.json @@ -1,3 +1,8 @@ { + "version": 2, + "installCommand": "npm ci --workspaces", + "buildCommand": "npm run -w packages/core build && npm run -w packages/react build && npm run -w app build", + "outputDirectory": "app/dist", "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] } +