diff --git a/.nvmrc b/.nvmrc index 41d9dbcf..2edeafb0 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.18.1 \ No newline at end of file +20 \ No newline at end of file diff --git a/packages/demo/backend/src/types/service.ts b/packages/demo/backend/src/types/service.ts index 64a9f171..c2df2eca 100644 --- a/packages/demo/backend/src/types/service.ts +++ b/packages/demo/backend/src/types/service.ts @@ -12,7 +12,7 @@ export interface WalletData { /** Wallet address */ address: Address /** Wallet ID */ - id: string + // id: string } /** diff --git a/packages/demo/frontend/package.json b/packages/demo/frontend/package.json index 98c48644..45c40ce7 100644 --- a/packages/demo/frontend/package.json +++ b/packages/demo/frontend/package.json @@ -22,7 +22,9 @@ "@eth-optimism/verbs-service": "workspace:*", "@eth-optimism/viem": "^0.4.13", "@privy-io/react-auth": "^2.24.0", + "@turnkey/react-wallet-kit": "^1.1.2", "buffer": "^6.0.3", + "eventemitter3": "^5", "react": "^18", "react-dom": "^18", "react-router": "7", @@ -31,6 +33,7 @@ "zod": "^4.0.5" }, "devDependencies": { + "@tailwindcss/postcss": "^4.1.13", "@testing-library/jest-dom": "^6.1.4", "@testing-library/react": "^14.1.2", "@testing-library/user-event": "^14.5.1", @@ -44,7 +47,8 @@ "globals": "^15.0.0", "jsdom": "^23.0.1", "postcss": "^8.4.31", - "tailwindcss": "^3.3.3", + "postcss-import": "^16.1.1", + "tailwindcss": "^4.1.13", "typescript": "^5.2.2", "typescript-eslint": "^8.0.0", "vite": "^4.4.5", diff --git a/packages/demo/frontend/postcss.config.js b/packages/demo/frontend/postcss.config.js index 2e7af2b7..b8e6b49a 100644 --- a/packages/demo/frontend/postcss.config.js +++ b/packages/demo/frontend/postcss.config.js @@ -1,6 +1,7 @@ +/** @type {import('postcss-load-config').Config} */ export default { plugins: { - tailwindcss: {}, autoprefixer: {}, + '@tailwindcss/postcss': {}, }, } diff --git a/packages/demo/frontend/src/App.tsx b/packages/demo/frontend/src/App.tsx index e0538a2a..b323ad58 100644 --- a/packages/demo/frontend/src/App.tsx +++ b/packages/demo/frontend/src/App.tsx @@ -1,11 +1,11 @@ import { BrowserRouter as Router, Routes, Route } from 'react-router-dom' import Terminal from './components/Terminal' import Home from './components/Home' -import { PrivyProvider } from './providers/PrivyProvider' +import { Turnkey as TurnkeyProvider } from './providers/TurnkeyProvider' function App() { return ( - +
@@ -14,7 +14,7 @@ function App() {
-
+ ) } diff --git a/packages/demo/frontend/src/abis/mintableErc20Abi.ts b/packages/demo/frontend/src/abis/mintableErc20Abi.ts new file mode 100644 index 00000000..3eb1cc25 --- /dev/null +++ b/packages/demo/frontend/src/abis/mintableErc20Abi.ts @@ -0,0 +1,347 @@ +export const mintableErc20Abi = [ + { + inputs: [], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [ + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'allowance', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'needed', + type: 'uint256', + }, + ], + name: 'ERC20InsufficientAllowance', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'sender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'balance', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'needed', + type: 'uint256', + }, + ], + name: 'ERC20InsufficientBalance', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'approver', + type: 'address', + }, + ], + name: 'ERC20InvalidApprover', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'receiver', + type: 'address', + }, + ], + name: 'ERC20InvalidReceiver', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'ERC20InvalidSender', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + ], + name: 'ERC20InvalidSpender', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'Approval', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'from', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'to', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'Transfer', + type: 'event', + }, + { + inputs: [ + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + ], + name: 'allowance', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'approve', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'balanceOf', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'burn', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'decimals', + outputs: [ + { + internalType: 'uint8', + name: '', + type: 'uint8', + }, + ], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'mint', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'name', + outputs: [ + { + internalType: 'string', + name: '', + type: 'string', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'symbol', + outputs: [ + { + internalType: 'string', + name: '', + type: 'string', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalSupply', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'transfer', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'from', + type: 'address', + }, + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'transferFrom', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + ] + \ No newline at end of file diff --git a/packages/demo/frontend/src/components/Home.tsx b/packages/demo/frontend/src/components/Home.tsx index 77e8ae84..0b6cd430 100644 --- a/packages/demo/frontend/src/components/Home.tsx +++ b/packages/demo/frontend/src/components/Home.tsx @@ -73,7 +73,7 @@ function Home() {

Overview

-
+

Features

-
+
{/* Core Capabilities Grid */} @@ -445,7 +445,7 @@ function Home() {

Getting Started

-
+
- Loading... - - ) - } - - if (authenticated) { + if (authState === AuthState.Authenticated) { return (
- {user?.email?.address || 'Connected'} + {user?.userEmail || 'Connected'} diff --git a/packages/demo/frontend/src/components/Terminal.tsx b/packages/demo/frontend/src/components/Terminal.tsx index 08899b66..951a4260 100644 --- a/packages/demo/frontend/src/components/Terminal.tsx +++ b/packages/demo/frontend/src/components/Terminal.tsx @@ -4,25 +4,20 @@ import { chainById } from '@eth-optimism/viem/chains' import { Buffer } from 'buffer' if (!globalThis.Buffer) globalThis.Buffer = Buffer -import { useState, useEffect, useRef, useMemo, useCallback } from 'react' -import { - useWallets, - usePrivy, - type WalletWithMetadata, - useUser as usePrivyUser, - useSessionSigners, - useUser, -} from '@privy-io/react-auth' +import { useState, useEffect, useRef } from 'react' +import { AuthState, ClientState, useTurnkey, WalletSource, type EmbeddedWallet, type TurnkeySDKClientBase } from "@turnkey/react-wallet-kit"; import type { - CreateWalletResponse, - GetAllWalletsResponse, WalletData, } from '@eth-optimism/verbs-service' import NavBar from './NavBar' -import { PrivyAuthButton } from './PrivyAuthButton' +import { TurnkeyAuthButton } from './PrivyAuthButton' import { verbsApi } from '../api/verbsApi' -import type { Address } from 'viem' -import { env } from '../envVars' +import { encodeFunctionData, formatUnits, type Address } from 'viem' +import { getVerbs } from '../config/verbs'; +import { SUPPORTED_TOKENS, type SmartWallet, type SupportedChainId, type TokenBalance } from '@eth-optimism/verbs-sdk/react'; +import { mintableErc20Abi } from '../abis/mintableErc20Abi'; +import { baseSepolia, unichain } from 'viem/chains' +import { env } from '../envVars'; interface TerminalLine { id: string type: 'input' | 'output' | 'error' | 'success' | 'warning' @@ -101,76 +96,61 @@ const Terminal = () => { const [commandHistory, setCommandHistory] = useState([]) const [historyIndex, setHistoryIndex] = useState(-1) const [pendingPrompt, setPendingPrompt] = useState(null) - const [selectedWallet, setSelectedWallet] = useState(null) - const [screenWidth, setScreenWidth] = useState( - typeof window !== 'undefined' ? window.innerWidth : 1200, - ) - const [currentWalletList, setCurrentWalletList] = useState< - GetAllWalletsResponse['wallets'] | null - >(null) + const [selectedWallet, setSelectedWallet] = useState(null) const inputRef = useRef(null) const terminalRef = useRef(null) - const { authenticated: isSignedIn, getAccessToken } = usePrivy() - const { user } = useUser() - const getAuthHeaders = useCallback(async () => { - const token = await getAccessToken() - return token ? { Authorization: `Bearer ${token}` } : undefined - }, [getAccessToken]) - // Privy wallet hooks - const { wallets } = useWallets() - const { authenticated: privyAuthenticated } = usePrivy() - const { user: privyUser } = usePrivyUser() - const { addSessionSigners } = useSessionSigners() - const ethereumEmbeddedWallets = useMemo( - () => - (privyUser?.linkedAccounts?.filter( - (account) => - account.type === 'wallet' && - account.walletClientType === 'privy' && - account.chainType === 'ethereum', - ) as WalletWithMetadata[]) ?? [], - [privyUser], - ) + const { wallets, clientState, authState, user, createWallet, refreshWallets, httpClient, session } = useTurnkey(); const [selectedVaultIndex, setSelectedVaultIndex] = useState(0) + const embeddedWallet = wallets.find( + (wallet) => wallet.accounts.some((account) => account.addressFormat === 'ADDRESS_FORMAT_ETHEREUM') && wallet.source === WalletSource.Embedded, + ) as EmbeddedWallet | undefined + + useEffect(() => { + async function createEmbeddedWallet() { + const wallet = await createWallet({ + walletName: `My New Wallet ${Math.random().toString(36).substring(2, 15)}`, + accounts: ["ADDRESS_FORMAT_ETHEREUM"], + }) + console.log('wallet created', wallet) + refreshWallets() + } + + if (clientState === ClientState.Ready && authState === AuthState.Authenticated && !embeddedWallet) { + createEmbeddedWallet() + } + }, [clientState, authState]) + // Auto-select wallet when Privy is authenticated and has wallets useEffect(() => { const autoSelectWallet = async () => { if ( - isSignedIn && - privyAuthenticated && + authState === AuthState.Authenticated && wallets.length > 0 && - !selectedWallet + !selectedWallet && + embeddedWallet && + httpClient && + session?.organizationId ) { - // Use the first available embedded wallet - const embeddedWallet = wallets.find( - (wallet) => wallet.walletClientType === 'privy', - ) - if (embeddedWallet) { - const walletAddress = embeddedWallet.address - - setSelectedWallet({ - id: user?.id || 'unknown', - address: walletAddress as `0x${string}`, - }) - - // Add a success message to the terminal - const welcomeLine: TerminalLine = { - id: `welcome-${Date.now()}`, - type: 'success', - content: `Welcome back ${user?.email?.address || user?.id}!\nWallet auto-selected: ${walletAddress}`, - timestamp: new Date(), - } - setLines((prev) => [...prev, welcomeLine]) - } + const initializeVerbsWallet = async () => { + const verbs = getVerbs() + console.log('session!.organizationId', session.organizationId) + const verbsWallet = await verbs.wallet.hostedWalletToVerbsWallet({client: httpClient, organizationId: session.organizationId, signWith: embeddedWallet.accounts[0].address, ethereumAddress: embeddedWallet.accounts[0].address}) + const wallet = await verbs.wallet.getSmartWallet({ + signer: verbsWallet.signer, + deploymentOwners: [verbsWallet.address], + }) + setSelectedWallet(wallet) + } + initializeVerbsWallet() } } autoSelectWallet() - }, [isSignedIn, privyAuthenticated, wallets.length, selectedWallet, user]) + }, [wallets.length, selectedWallet, user, embeddedWallet, authState, clientState]) // Function to render content with clickable links const renderContentWithLinks = (content: string) => { @@ -202,37 +182,75 @@ const Terminal = () => { } // DRY function to format balance strings - const formatBalance = (balance: string, decimals: number): string => { - const balanceBigInt = BigInt(balance) - const divisor = BigInt(10 ** decimals) - const wholePart = balanceBigInt / divisor - const fractionalPart = balanceBigInt % divisor - - if (fractionalPart === 0n) { - return wholePart.toString() - } + // const formatBalance = (balance: string, decimals: number): string => { + // const balanceBigInt = BigInt(balance) + // const divisor = BigInt(10 ** decimals) + // const wholePart = balanceBigInt / divisor + // const fractionalPart = balanceBigInt % divisor - const fractionalStr = fractionalPart.toString().padStart(decimals, '0') - const trimmedFractional = fractionalStr.replace(/0+$/, '') + // if (fractionalPart === 0n) { + // return wholePart.toString() + // } - if (trimmedFractional === '') { - return wholePart.toString() - } + // const fractionalStr = fractionalPart.toString().padStart(decimals, '0') + // const trimmedFractional = fractionalStr.replace(/0+$/, '') - return `${wholePart}.${trimmedFractional}` - } + // if (trimmedFractional === '') { + // return wholePart.toString() + // } + + // return `${wholePart}.${trimmedFractional}` + // } // DRY function to display wallet balance with loading state const displayWalletBalance = async ( - walletId: string, + wallet: SmartWallet, showVaultPositions: boolean = true, ): Promise => { - const result = await verbsApi.getWalletBalance( - walletId, - await getAuthHeaders(), + try { + // For manual/backend wallets, use the original API call + const result = await wallet.getBalance() + const verbs = getVerbs() + const vaults = await verbs.lend.getMarkets() + const vaultBalances = await Promise.all( + vaults.map(async (vault) => { + try { + const vaultBalance = await wallet.lend!.getPosition( + { marketId: { address: vault.address, chainId: vault.chainId as SupportedChainId } }, + ) + + // Only include vaults with non-zero balances + if (vaultBalance.balance > 0n) { + // Create a TokenBalance object for the vault + const formattedBalance = formatUnits(vaultBalance.balance, 6) // Assuming 6 decimals for vault shares + return { + symbol: `${vault.name}`, + totalBalance: vaultBalance.balance, + totalFormattedBalance: formattedBalance, + chainBalances: [ + { + chainId: vaultBalance.chainId, + balance: vaultBalance.balance, + tokenAddress: vault.asset, + formattedBalance: formattedBalance, + }, + ], + } as TokenBalance + } + return null + } catch (error) { + console.error(error) + return null + } + }), + ) + // Filter out null values and add vault balances to token balances + const validVaultBalances = vaultBalances.filter( + (balance): balance is NonNullable => balance !== null, ) + const allBalances = [...result, ...validVaultBalances] - const balancesByChain = result.balance.reduce( + const balancesByChain = allBalances.reduce( (acc, token) => { token.chainBalances.forEach( ({ chainId, balance, formattedBalance }) => { @@ -248,7 +266,7 @@ const Terminal = () => { ) return acc }, - {} as Record, + {} as Record, ) const balanceLines: string[] = [] @@ -287,9 +305,9 @@ const Terminal = () => { ) balanceLines.push( - ` ETH: ${ethBalance ? formatBalance(ethBalance.totalBalance, 18) : '0'}`, - ` USDC: ${usdcBalance ? formatBalance(usdcBalance.totalBalance, 6) : '0'}`, - ` USDC_DEMO: ${usdcDemoBalance ? formatBalance(usdcDemoBalance.totalBalance, 6) : '0'}`, + ` ETH: ${ethBalance ? ethBalance.totalFormattedBalance : '0'}`, + ` USDC: ${usdcBalance ? usdcBalance.totalFormattedBalance : '0'}`, + ` USDC_DEMO: ${usdcDemoBalance ? usdcDemoBalance.totalFormattedBalance : '0'}`, ) // Add vault balances if any exist and showVaultPositions is true @@ -297,7 +315,7 @@ const Terminal = () => { balanceLines.push(' Vault Positions:') vaultBalances.forEach((vault) => { balanceLines.push( - ` ${vault.symbol}: ${formatBalance(vault.totalBalance, 6)}`, + ` ${vault.symbol}: ${vault.totalFormattedBalance}`, ) // Assume 6 decimals for vault shares }) } @@ -306,6 +324,10 @@ const Terminal = () => { } return balanceLines.join('\n') + } catch (error) { + console.error(error) + return 'Error fetching wallet balance' + } } // Focus input on mount and keep it focused @@ -315,92 +337,6 @@ const Terminal = () => { } }, []) - // Track screen width changes - useEffect(() => { - const handleResize = () => { - setScreenWidth(window.innerWidth) - } - - window.addEventListener('resize', handleResize) - return () => window.removeEventListener('resize', handleResize) - }, []) - - // Update wallet list display when screen size changes - useEffect(() => { - if (currentWalletList && pendingPrompt?.type === 'walletSelectSelection') { - // Find the wallet selection line and update it - setLines((prev) => { - const walletSelectIndex = prev.findIndex((line) => - line.content.includes('Select a wallet:'), - ) - - if (walletSelectIndex !== -1) { - const formatWalletColumns = ( - wallets: GetAllWalletsResponse['wallets'], - ) => { - const lines: string[] = [] - const totalWallets = wallets.length - - // Responsive column logic: 1 on mobile, 2 on tablet, 3 on desktop - const isMobile = screenWidth < 480 - const isTablet = screenWidth >= 480 && screenWidth < 768 - - const numColumns = isMobile ? 1 : isTablet ? 2 : 3 - const walletsPerColumn = Math.ceil(totalWallets / numColumns) - const columnWidth = isMobile ? 0 : isTablet ? 25 : 33 // Tighter spacing for 2 cols - - for (let row = 0; row < walletsPerColumn; row++) { - let line = '' - - for (let col = 0; col < numColumns; col++) { - const walletIndex = col * walletsPerColumn + row - - if (walletIndex < totalWallets) { - const wallet = wallets[walletIndex] - const num = walletIndex + 1 - const numStr = num < 10 ? ` ${num}` : `${num}` - const addressDisplay = `${wallet.address.slice(0, 6)}...${wallet.address.slice(-4)}` - const selected = - selectedWallet?.id === wallet.id ? ' (selected)' : '' - const columnText = `${numStr}. ${addressDisplay}${selected}` - - // Add column text and pad for next column (except last column) - if (col < numColumns - 1) { - line += columnText.padEnd(columnWidth) - } else { - line += columnText - } - } - } - - // Only add non-empty lines - if (line.trim()) { - lines.push(line) - } - } - - return lines.join('\n') - } - - const walletOptions = formatWalletColumns(currentWalletList) - - const updatedLine = { - ...prev[walletSelectIndex], - content: `Select a wallet:\n\n${walletOptions}\n\nEnter wallet number:`, - } - - return [ - ...prev.slice(0, walletSelectIndex), - updatedLine, - ...prev.slice(walletSelectIndex + 1), - ] - } - - return prev - }) - } - }, [screenWidth, currentWalletList, pendingPrompt, selectedWallet]) - // Keep terminal scrolled to bottom useEffect(() => { if (terminalRef.current) { @@ -471,98 +407,18 @@ const Terminal = () => { initializeTerminal() }, []) - const createWallet = async ( - userId: string, - ): Promise => { - return verbsApi.createWallet(userId) - } - - const getAllWallets = async (): Promise => { - return verbsApi.getAllWallets() - } - - const addSessionSigner = useCallback( - async (walletAddress: string) => { - if (!env.VITE_SESSION_SIGNER_ID) { - console.error('SESSION_SIGNER_ID must be defined to addSessionSigner') - return - } - console.log( - 'Adding session signer for wallet:', - env.VITE_SESSION_SIGNER_ID, - ) - console.log('wallet address', walletAddress) - - try { - await addSessionSigners({ - address: walletAddress, - signers: [ - { - signerId: env.VITE_SESSION_SIGNER_ID, - }, - ], - }) - console.log('Session signer added for wallet:', walletAddress) - } catch (error) { - console.error('Error adding session signer:', error) - console.log('error stack', (error as Error).stack) - } - }, - [addSessionSigners], - ) - - useEffect(() => { - const undelegatedEthereumEmbeddedWallets = ethereumEmbeddedWallets.filter( - (wallet) => wallet.delegated !== true, - ) - undelegatedEthereumEmbeddedWallets.forEach((wallet) => { - addSessionSigner(wallet.address) - }) - }, [ethereumEmbeddedWallets]) - const processCommand = (command: string) => { const trimmed = command.trim() if (!trimmed) return // Handle pending prompts if (pendingPrompt) { - if (pendingPrompt.type === 'userId') { - handleWalletCreation(trimmed) - return - } else if (pendingPrompt.type === 'lendVault') { + if (pendingPrompt.type === 'lendVault') { handleLendVaultSelection((pendingPrompt.data as VaultData[]) || []) return } else if (pendingPrompt.type === 'lendAmount') { handleLendAmountSubmission(parseFloat(trimmed)) return - } else if (pendingPrompt.type === 'walletSendSelection') { - handleWalletSendSelection( - parseInt(trimmed), - (pendingPrompt.data as WalletData[]) || [], - ) - return - } else if (pendingPrompt.type === 'walletSendAmount') { - handleWalletSendAmount( - parseFloat(trimmed), - pendingPrompt.data as { selectedWallet: WalletData; balance: number }, - ) - return - } else if (pendingPrompt.type === 'walletSendRecipient') { - handleWalletSendRecipient( - trimmed, - pendingPrompt.data as { - selectedWallet: WalletData - balance: number - amount: number - }, - ) - return - } else if (pendingPrompt.type === 'walletSelectSelection') { - handleWalletSelectSelection( - parseInt(trimmed), - (pendingPrompt.data as WalletData[]) || [], - ) - return } } @@ -593,40 +449,6 @@ const Terminal = () => { case 'clear': setLines([]) return - case 'wallet create': - setLines((prev) => [...prev, commandLine]) - if (isSignedIn) { - const warningLine: TerminalLine = { - id: `wallet-create-disabled-${Date.now()}`, - type: 'warning', - content: - 'Wallet creation is disabled while you are logged in. Please log out to use this command. While logged in, the embedded wallet associated with your user account is used.', - timestamp: new Date(), - } - setLines((prev) => [...prev, warningLine]) - return - } - setPendingPrompt({ - type: 'userId', - message: 'Enter unique userId:', - }) - return - case 'wallet select': - case 'select': - setLines((prev) => [...prev, commandLine]) - if (isSignedIn) { - const warningLine: TerminalLine = { - id: `wallet-select-disabled-${Date.now()}`, - type: 'warning', - content: - 'Wallet selection is disabled while you are logged in. Please log out to use this command. While logged in, the embedded wallet associated with your user account is used.', - timestamp: new Date(), - } - setLines((prev) => [...prev, warningLine]) - return - } - handleWalletSelect() - return case 'wallet balance': case 'balance': { setLines((prev) => [...prev, commandLine]) @@ -639,12 +461,6 @@ const Terminal = () => { handleWalletFund() return } - case 'wallet send': - case 'send': { - setLines((prev) => [...prev, commandLine]) - handleWalletSendList() - return - } case 'wallet lend': case 'lend': { setLines((prev) => [...prev, commandLine]) @@ -697,71 +513,6 @@ Active Wallets: 0`, setLines((prev) => [...prev, commandLine, response]) } - const handleWalletCreation = async (userId: string) => { - const userInputLine: TerminalLine = { - id: `input-${Date.now()}`, - type: 'input', - content: `Enter userId for the new wallet: ${userId}`, - timestamp: new Date(), - } - - const loadingLine: TerminalLine = { - id: `loading-${Date.now()}`, - type: 'output', - content: 'Creating wallet...', - timestamp: new Date(), - } - - setLines((prev) => [...prev, userInputLine, loadingLine]) - setPendingPrompt(null) - - try { - const result = await createWallet(userId) - - const successLine: TerminalLine = { - id: `success-${Date.now()}`, - type: 'success', - content: `Wallet created successfully! -Privy Address: ${result.privyAddress} -Smart Wallet Address: ${result.smartWalletAddress} -User ID: ${result.userId}`, - timestamp: new Date(), - } - - // Auto-select the newly created wallet - try { - const all = await getAllWallets() - const created = all.wallets.find( - (w) => - w.address.toLowerCase() === - (result.smartWalletAddress || '').toLowerCase() || - w.address.toLowerCase() === - (result.privyAddress || '').toLowerCase(), - ) - - const walletToSelect = created || all.wallets[all.wallets.length - 1] - if (walletToSelect) { - setSelectedWallet(walletToSelect) - } - } catch { - // ignore selection errors silently - } - - setLines((prev) => [...prev.slice(0, -1), successLine]) - } catch (error) { - const errorLine: TerminalLine = { - id: `error-${Date.now()}`, - type: 'error', - content: `Failed to create wallet: ${ - error instanceof Error ? error.message : 'Unknown error' - }`, - timestamp: new Date(), - } - - setLines((prev) => [...prev.slice(0, -1), errorLine]) - } - } - const handleLendVaultSelection = async (vaults: VaultData[]) => { // Use the selectedVaultIndex to select the vault const selectedVault = vaults[selectedVaultIndex] @@ -836,18 +587,16 @@ User ID: ${result.userId}`, } setLines((prev) => [...prev, loadingBalanceLine]) - // Get wallet balance using DRY function - const walletBalanceText = await displayWalletBalance( - selectedWallet!.id, - true, - ) + const walletBalanceResult = await selectedWallet?.getBalance() - // Get single-chain token balance for the vault's asset (e.g., USDC on the vault's chain) - const walletBalanceResult = await verbsApi.getWalletBalance( - selectedWallet!.id, - await getAuthHeaders(), - ) - const chainToken = walletBalanceResult.balance + const chainToken = walletBalanceResult?.reduce((acc, token) => { + token.chainBalances.forEach((chainBalance) => { + if (chainBalance.chainId === selectedVault.chainId && chainBalance.tokenAddress === selectedVault.asset) { + acc.push({ ...chainBalance, symbol: token.symbol, totalBalance: chainBalance.balance, totalFormattedBalance: chainBalance.formattedBalance, chainBalances: [chainBalance] }) + } + }) + return acc + }, [] as TokenBalance[]) .flatMap((t) => t.chainBalances.map((cb) => ({ ...cb }))) .find( (cb) => @@ -865,7 +614,7 @@ User ID: ${result.userId}`, // Show balances and ask for lend amount const balancesDisplay = `Wallet Balance: -${walletBalanceText} +${walletBalanceResult?.map((token) => `${token.symbol}: ${token.totalFormattedBalance}`).join('\n')} How much would you like to lend?` @@ -945,14 +694,33 @@ How much would you like to lend?` try { console.log('[FRONTEND] Calling openLendPosition API') - const result = await verbsApi.openLendPosition( - promptData.selectedWallet.id, - amount, - promptData.selectedVault.asset as Address, - promptData.selectedVault.chainId, - promptData.selectedVault.address as Address, - await getAuthHeaders(), + const asset = SUPPORTED_TOKENS.find( + (token) => token.address[promptData.selectedVault.chainId as SupportedChainId] === promptData.selectedVault.asset, ) + if (!asset) { + throw new Error(`Asset not found for token address: ${promptData.selectedVault.asset}`) + } + console.log('[FRONTEND] Calling lendDeposit API') + + const depositHash = await selectedWallet!.lend!.openPosition({ + amount: amount, + asset: asset, + marketId: { address: promptData.selectedVault.address as Address, chainId: promptData.selectedVault.chainId as SupportedChainId }, + }) + const innerResult = { + hash: depositHash, + blockExplorerUrl: getBlockExplorerUrl(promptData.selectedVault.chainId as SupportedChainId), + } + const result = { + transaction: { + blockExplorerUrl: innerResult.blockExplorerUrl, + hash: innerResult.hash, + amount: amount.toString(), + asset: asset, + marketId: { address: promptData.selectedVault.address as Address, chainId: promptData.selectedVault.chainId as SupportedChainId }, + apy: promptData.selectedVault.apy, + }, + } console.log( '[FRONTEND] Lend deposit successful:', @@ -982,199 +750,6 @@ Tx: ${result.transaction.blockExplorerUrl}/${result.transaction.hash || 'pen } } - const handleWalletSelect = async () => { - const loadingLine: TerminalLine = { - id: `loading-${Date.now()}`, - type: 'output', - content: 'Loading wallets...', - timestamp: new Date(), - } - - setLines((prev) => [...prev, loadingLine]) - - try { - const result = await getAllWallets() - console.log(result) - - if (result.wallets.length === 0) { - const emptyLine: TerminalLine = { - id: `empty-${Date.now()}`, - type: 'error', - content: 'No wallets available. Create one with "wallet create".', - timestamp: new Date(), - } - setLines((prev) => [...prev.slice(0, -1), emptyLine]) - return - } - - // Format wallets in responsive columns - const formatWalletColumns = ( - wallets: GetAllWalletsResponse['wallets'], - ) => { - const lines: string[] = [] - const totalWallets = wallets.length - - // Responsive column logic: 1 on mobile, 2 on tablet, 3 on desktop - const isMobile = screenWidth < 480 - const isTablet = screenWidth >= 480 && screenWidth < 768 - - const numColumns = isMobile ? 1 : isTablet ? 2 : 3 - const walletsPerColumn = Math.ceil(totalWallets / numColumns) - const columnWidth = isMobile ? 0 : isTablet ? 25 : 33 // Tighter spacing for 2 cols - - for (let row = 0; row < walletsPerColumn; row++) { - let line = '' - - for (let col = 0; col < numColumns; col++) { - const walletIndex = col * walletsPerColumn + row - - if (walletIndex < totalWallets) { - const wallet = wallets[walletIndex] - const num = walletIndex + 1 - const numStr = num < 10 ? ` ${num}` : `${num}` - const addressDisplay = `${wallet.address.slice(0, 6)}...${wallet.address.slice(-4)}` - const selected = - selectedWallet?.id === wallet.id ? ' (selected)' : '' - const columnText = `${numStr}. ${addressDisplay}${selected}` - - // Add column text and pad for next column (except last column) - if (col < numColumns - 1) { - line += columnText.padEnd(columnWidth) - } else { - line += columnText - } - } - } - - // Only add non-empty lines - if (line.trim()) { - lines.push(line) - } - } - - return lines.join('\n') - } - - const walletOptions = formatWalletColumns(result.wallets) - - const walletSelectionLine: TerminalLine = { - id: `wallet-select-${Date.now()}`, - type: 'output', - content: `Select a wallet:\n\n${walletOptions}\n\nEnter wallet number:`, - timestamp: new Date(), - } - - setLines((prev) => [...prev.slice(0, -1), walletSelectionLine]) - setCurrentWalletList(result.wallets) - setPendingPrompt({ - type: 'walletSelectSelection', - message: '', - data: result.wallets, - }) - } catch (error) { - console.log(error) - const errorLine: TerminalLine = { - id: `error-${Date.now()}`, - type: 'error', - content: `Failed to load wallets: ${ - error instanceof Error ? error.message : 'Unknown error' - }`, - timestamp: new Date(), - } - setLines((prev) => [...prev.slice(0, -1), errorLine]) - } - } - - const handleWalletSelectSelection = async ( - selection: number, - wallets: WalletData[], - ) => { - setPendingPrompt(null) - setCurrentWalletList(null) - - if (isNaN(selection) || selection < 1 || selection > wallets.length) { - const errorLine: TerminalLine = { - id: `error-${Date.now()}`, - type: 'error', - content: `Invalid selection. Please enter a number between 1 and ${wallets.length}.`, - timestamp: new Date(), - } - setLines((prev) => [...prev, errorLine]) - return - } - - const selectedWalletData = wallets[selection - 1] - setSelectedWallet(selectedWalletData) - - // Clear the wallet selection list and replace with just the success message - setLines((prev) => { - // Find the index of the "Select a wallet:" line and remove everything from there - const selectWalletIndex = prev.findIndex((line) => - line.content.includes('Select a wallet:'), - ) - - if (selectWalletIndex !== -1) { - // Keep everything before the wallet selection list - const beforeSelection = prev.slice(0, selectWalletIndex) - - // Add just the success message - const successLine: TerminalLine = { - id: `select-success-${Date.now()}`, - type: 'success', - content: `Wallet selected:\n${selectedWalletData.address}`, - timestamp: new Date(), - } - - return [...beforeSelection, successLine] - } - - // Fallback: just add the success line if we can't find the selection - const successLine: TerminalLine = { - id: `select-success-${Date.now()}`, - type: 'success', - content: `Wallet selected:\n${selectedWalletData.address}`, - timestamp: new Date(), - } - return [...prev, successLine] - }) - - // Automatically fetch and display balance for the selected wallet - setTimeout(async () => { - // Add loading message - const loadingLine: TerminalLine = { - id: `loading-balance-${Date.now()}`, - type: 'output', - content: 'Loading balance...', - timestamp: new Date(), - } - setLines((prev) => [...prev, loadingLine]) - - try { - const balanceText = await displayWalletBalance( - selectedWalletData.id, - true, - ) - - const balanceLine: TerminalLine = { - id: `balance-${Date.now()}`, - type: 'output', - content: `\n${balanceText}`, - timestamp: new Date(), - } - setLines((prev) => [...prev.slice(0, -1), balanceLine]) // Replace loading message - } catch { - // Replace loading message with error - const errorLine: TerminalLine = { - id: `balance-error-${Date.now()}`, - type: 'error', - content: 'Failed to load balance', - timestamp: new Date(), - } - setLines((prev) => [...prev.slice(0, -1), errorLine]) - } - }, 100) - } - const handleWalletBalance = async () => { if (!selectedWallet) { const errorLine: TerminalLine = { @@ -1198,7 +773,7 @@ Tx: ${result.transaction.blockExplorerUrl}/${result.transaction.hash || 'pen setLines((prev) => [...prev, loadingLine]) try { - const balanceText = await displayWalletBalance(selectedWallet.id, true) + const balanceText = await displayWalletBalance(selectedWallet, true) const successLine: TerminalLine = { id: `success-${Date.now()}`, @@ -1244,10 +819,7 @@ Tx: ${result.transaction.blockExplorerUrl}/${result.transaction.hash || 'pen setLines((prev) => [...prev, fundingInfo]) try { - const { amount } = await verbsApi.fundWallet( - selectedWallet.id, - await getAuthHeaders(), - ) + const { amount } = await fundWallet(selectedWallet) const fundSuccessLine: TerminalLine = { id: `fund-success-${Date.now()}`, @@ -1257,7 +829,7 @@ Tx: ${result.transaction.blockExplorerUrl}/${result.transaction.hash || 'pen } setLines((prev) => [...prev, fundSuccessLine]) - const balanceText = await displayWalletBalance(selectedWallet.id, true) + const balanceText = await displayWalletBalance(selectedWallet, true) const balanceSuccessLine: TerminalLine = { id: `balance-success-${Date.now()}`, type: 'success', @@ -1290,17 +862,11 @@ Tx: ${result.transaction.blockExplorerUrl}/${result.transaction.hash || 'pen } // Check if selected wallet has USDC balance before proceeding try { - const balanceResult = await verbsApi.getWalletBalance( - selectedWallet.id, - await getAuthHeaders(), - ) + const balanceResult = await getWalletBalance(selectedWallet) const usdcTokens = balanceResult.balance.filter( (token) => token.symbol === 'USDC' || token.symbol === 'USDC_DEMO', ) - const usdcBalance = usdcTokens.reduce( - (acc, token) => acc + parseFloat(token.totalBalance), - 0, - ) + const usdcBalance = usdcTokens.reduce((acc, token) => acc + parseFloat(`${token.totalBalance}`), 0) if (usdcBalance <= 0) { const noBalanceLine: TerminalLine = { @@ -1382,276 +948,6 @@ Tx: ${result.transaction.blockExplorerUrl}/${result.transaction.hash || 'pen } } - const handleWalletSendList = async () => { - const loadingLine: TerminalLine = { - id: `loading-${Date.now()}`, - type: 'output', - content: 'Loading wallets...', - timestamp: new Date(), - } - - setLines((prev) => [...prev, loadingLine]) - - try { - const result = await getAllWallets() - - if (result.wallets.length === 0) { - const emptyLine: TerminalLine = { - id: `empty-${Date.now()}`, - type: 'error', - content: 'No wallets available. Create one with "wallet create".', - timestamp: new Date(), - } - setLines((prev) => [...prev.slice(0, -1), emptyLine]) - return - } - - // Get balances for all wallets - const walletsWithBalances = await Promise.all( - result.wallets.map(async (wallet) => { - try { - const balanceResult = await verbsApi.getWalletBalance( - wallet.id, - await getAuthHeaders(), - ) - const usdcToken = balanceResult.balance.find( - (token) => token.symbol === 'USDC', - ) - const usdcBalance = usdcToken - ? parseFloat(usdcToken.totalBalance) - : 0 - return { - ...wallet, - usdcBalance, - } - } catch { - return { - ...wallet, - usdcBalance: 0, - } - } - }), - ) - - // Filter wallets with USDC > 0 and sort by balance (highest first) - const walletsWithUSDC = walletsWithBalances - .filter((wallet) => wallet.usdcBalance > 0) - .sort((a, b) => b.usdcBalance - a.usdcBalance) - - if (walletsWithUSDC.length === 0) { - const noBalanceLine: TerminalLine = { - id: `no-balance-${Date.now()}`, - type: 'error', - content: 'No wallets have a USDC balance. Fund a wallet first.', - timestamp: new Date(), - } - setLines((prev) => [...prev.slice(0, -1), noBalanceLine]) - return - } - - // Create wallet options list - const walletOptions = walletsWithUSDC - .map((wallet, index) => { - const num = index + 1 - const numStr = num < 10 ? ` ${num}` : `${num}` - const addressDisplay = `${wallet.address.slice(0, 6)}...${wallet.address.slice(-4)}` - return `${numStr}. ${addressDisplay} - ${wallet.usdcBalance} USDC` - }) - .join('\n') - - const walletSelectionLine: TerminalLine = { - id: `wallet-send-selection-${Date.now()}`, - type: 'output', - content: `Select wallet to send from:\n\n${walletOptions}\n\nEnter wallet number:`, - timestamp: new Date(), - } - - setLines((prev) => [...prev.slice(0, -1), walletSelectionLine]) - setPendingPrompt({ - type: 'walletSendSelection', - message: '', - data: walletsWithUSDC.map((w) => ({ - id: w.id, - address: w.address as Address, - })), - }) - } catch (error) { - console.log(error) - const errorLine: TerminalLine = { - id: `error-${Date.now()}`, - type: 'error', - content: `Failed to load wallets: ${ - error instanceof Error ? error.message : 'Unknown error' - }`, - timestamp: new Date(), - } - setLines((prev) => [...prev.slice(0, -1), errorLine]) - } - } - - const handleWalletSendSelection = async ( - selection: number, - wallets: WalletData[], - ) => { - setPendingPrompt(null) - - if (isNaN(selection) || selection < 1 || selection > wallets.length) { - const errorLine: TerminalLine = { - id: `error-${Date.now()}`, - type: 'error', - content: `Invalid selection. Please enter a number between 1 and ${wallets.length}.`, - timestamp: new Date(), - } - setLines((prev) => [...prev, errorLine]) - return - } - - const selectedWallet = wallets[selection - 1] - - const loadingLine: TerminalLine = { - id: `loading-${Date.now()}`, - type: 'output', - content: 'Loading wallet balance...', - timestamp: new Date(), - } - - setLines((prev) => [...prev, loadingLine]) - - try { - const result = await verbsApi.getWalletBalance( - selectedWallet.id, - await getAuthHeaders(), - ) - const usdcToken = result.balance.find((token) => token.symbol === 'USDC') - const usdcBalance = usdcToken ? parseFloat(usdcToken.totalBalance) : 0 - - const balanceInfoLine: TerminalLine = { - id: `balance-info-${Date.now()}`, - type: 'success', - content: `Wallet ${shortenAddress(selectedWallet.address)} has ${usdcBalance} USDC available.\n\nEnter amount to send:`, - timestamp: new Date(), - } - setLines((prev) => [...prev.slice(0, -1), balanceInfoLine]) - - setPendingPrompt({ - type: 'walletSendAmount', - message: '', - data: { selectedWallet, balance: usdcBalance }, - }) - } catch (error) { - const errorLine: TerminalLine = { - id: `error-${Date.now()}`, - type: 'error', - content: `Failed to fetch wallet balance: ${ - error instanceof Error ? error.message : 'Unknown error' - }`, - timestamp: new Date(), - } - setLines((prev) => [...prev.slice(0, -1), errorLine]) - } - } - - const handleWalletSendAmount = async ( - amount: number, - data: { selectedWallet: WalletData; balance: number }, - ) => { - setPendingPrompt(null) - - if (isNaN(amount) || amount <= 0) { - const errorLine: TerminalLine = { - id: `error-${Date.now()}`, - type: 'error', - content: 'Invalid amount. Please enter a positive number.', - timestamp: new Date(), - } - setLines((prev) => [...prev, errorLine]) - return - } - - if (amount > data.balance) { - const errorLine: TerminalLine = { - id: `error-${Date.now()}`, - type: 'error', - content: `Insufficient balance. Available: ${data.balance} USDC`, - timestamp: new Date(), - } - setLines((prev) => [...prev, errorLine]) - return - } - - const amountConfirmLine: TerminalLine = { - id: `amount-confirm-${Date.now()}`, - type: 'output', - content: `Sending ${amount} USDC from ${shortenAddress(data.selectedWallet.address)}.\n\nEnter recipient address:`, - timestamp: new Date(), - } - setLines((prev) => [...prev, amountConfirmLine]) - - setPendingPrompt({ - type: 'walletSendRecipient', - message: '', - data: { ...data, amount }, - }) - } - - const handleWalletSendRecipient = async ( - recipientAddress: string, - data: { selectedWallet: WalletData; balance: number; amount: number }, - ) => { - setPendingPrompt(null) - - // Basic address validation - if ( - !recipientAddress || - !recipientAddress.startsWith('0x') || - recipientAddress.length !== 42 - ) { - const errorLine: TerminalLine = { - id: `error-${Date.now()}`, - type: 'error', - content: - 'Invalid address. Please enter a valid Ethereum address (0x...).', - timestamp: new Date(), - } - setLines((prev) => [...prev, errorLine]) - return - } - - const sendingLine: TerminalLine = { - id: `sending-${Date.now()}`, - type: 'output', - content: `Sending ${data.amount} USDC to ${shortenAddress(recipientAddress)}...`, - timestamp: new Date(), - } - - setLines((prev) => [...prev, sendingLine]) - - try { - const result = await verbsApi.sendTokens( - data.selectedWallet.id, - data.amount, - recipientAddress, - await getAuthHeaders(), - ) - - const successLine: TerminalLine = { - id: `send-success-${Date.now()}`, - type: 'success', - content: `Transaction created successfully!\n\nTo: ${result.transaction.to}\nValue: ${result.transaction.value}\nData: ${result.transaction.data.slice(0, 20)}...\n\nTransaction ready to be signed and sent.`, - timestamp: new Date(), - } - setLines((prev) => [...prev.slice(0, -1), successLine]) - } catch (error) { - const errorLine: TerminalLine = { - id: `error-${Date.now()}`, - type: 'error', - content: error instanceof Error ? error.message : 'Unknown error', - timestamp: new Date(), - } - setLines((prev) => [...prev.slice(0, -1), errorLine]) - } - } - const handleKeyDown = (e: React.KeyboardEvent) => { // Handle special keys for lend prompts if (pendingPrompt && pendingPrompt.type === 'lendVault') { @@ -1768,7 +1064,7 @@ Tx: ${result.transaction.blockExplorerUrl}/${result.transaction.hash || 'pen className="w-full h-full flex flex-col bg-terminal-bg shadow-terminal-inner cursor-text" onClick={handleClick} > - } /> + } /> {/* Terminal Content */}
setCurrentInput(e.target.value)} onKeyDown={handleKeyDown} - className="bg-transparent outline-none text-terminal-text caret-transparent flex-shrink-0" + className="bg-transparent outline-hidden text-terminal-text caret-transparent flex-shrink-0" style={{ width: `${Math.max(1, currentInput.length)}ch` }} autoComplete="off" spellCheck="false" @@ -1849,3 +1145,120 @@ Tx: ${result.transaction.blockExplorerUrl}/${result.transaction.hash || 'pen } export default Terminal + +function getBlockExplorerUrl(chainId: SupportedChainId) { + const chain = chainById[chainId] + if (!chain) { + throw new Error(`Chain not found for chainId: ${chainId}`) + } + if (chain.id === unichain.id) { + return 'https://unichain.blockscout.com/op' + } + if (chain.id === baseSepolia.id) { + return `https://base-sepolia.blockscout.com/op` + } + return chain.blockExplorers ? `${chain.blockExplorers?.default.url}/tx` : '' +} + +function serializeBigInt(obj: T): T { + return JSON.parse( + JSON.stringify(obj, (_key, value) => + typeof value === 'bigint' ? value.toString() : value, + ), + ) +} + +async function fundWallet(wallet: SmartWallet): Promise<{ + success: boolean + to: string + amount: string +}> { + const walletAddress = wallet.address + + const amountInDecimals = BigInt(Math.floor(parseFloat('100') * 1000000)) + + const usdcDemo = SUPPORTED_TOKENS.find((token) => token.metadata.symbol === 'USDC_DEMO') + if (!usdcDemo) { + throw new Error('USDC_DEMO not found') + } + + const calls = [ + { + to: usdcDemo.address[baseSepolia.id]!, + data: encodeFunctionData({ + abi: mintableErc20Abi, + functionName: 'mint', + args: [walletAddress, amountInDecimals], + }), + value: 0n, + }, + ] + + await wallet.sendBatch(calls, baseSepolia.id) + + return { + success: true, + to: walletAddress, + amount: formatUnits(amountInDecimals, 6), + } +} + +async function getWalletBalance(wallet: SmartWallet): Promise<{ balance: TokenBalance[] }> { + // Get regular token balances + const tokenBalances = await wallet.getBalance().catch((error) => { + console.error(error) + throw error + }) + + // Get market balances and add them to the response + const verbs = getVerbs() + let result: TokenBalance[] = [] + try { + const vaults = await verbs.lend.getMarkets() + + const vaultBalances = await Promise.all( + vaults.map(async (vault) => { + try { + const vaultBalance = await wallet.lend!.getPosition( + { marketId: { address: vault.address, chainId: vault.chainId as SupportedChainId } }, + ) + + // Only include vaults with non-zero balances + if (vaultBalance.balance > 0n) { + // Create a TokenBalance object for the vault + const formattedBalance = formatUnits(vaultBalance.balance, 6) // Assuming 6 decimals for vault shares + return { + symbol: `${vault.name}`, + totalBalance: vaultBalance.balance, + totalFormattedBalance: formattedBalance, + chainBalances: [ + { + chainId: vaultBalance.chainId, + balance: vaultBalance.balance, + tokenAddress: vault.asset, + formattedBalance: formattedBalance, + }, + ], + } as TokenBalance + } + return null + } catch (error) { + console.error(error) + return null + } + }), + ) + + // Filter out null values and add vault balances to token balances + const validVaultBalances = vaultBalances.filter( + (balance): balance is NonNullable => balance !== null, + ) + + result = [...tokenBalances, ...validVaultBalances] + } catch { + // Return just token balances if vault balance fetching fails + result = tokenBalances + } + + return { balance: serializeBigInt(result) } +} diff --git a/packages/demo/frontend/src/config/verbs.ts b/packages/demo/frontend/src/config/verbs.ts new file mode 100644 index 00000000..363f2600 --- /dev/null +++ b/packages/demo/frontend/src/config/verbs.ts @@ -0,0 +1,111 @@ +import { createVerbs, type ReactVerbsConfig, type LendMarketConfig, type Asset } from '@eth-optimism/verbs-sdk/react' +import type { Address } from 'viem' + +import { baseSepolia, mainnet, unichain } from 'viem/chains' + +import { env } from '../envVars.js' + +export const USDC: Asset = { + address: { + [mainnet.id]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + [unichain.id]: '0x078d782b760474a361dda0af3839290b0ef57ad6', + [baseSepolia.id]: '0x036CbD53842c5426634e7929541eC2318f3dCF7e', + }, + metadata: { + decimals: 6, + name: 'USDC', + symbol: 'USDC', + }, + type: 'erc20', + } + + export const USDC_DEMO: Asset = { + address: { + [baseSepolia.id]: '0x87c25229afbc30418d0144e8dfb2bcf8efd92c6c', + }, + metadata: { + decimals: 6, + name: 'USDC Demo', + symbol: 'USDC_DEMO', + }, + type: 'erc20', + } + +export const GauntletUSDC: LendMarketConfig = { + address: '0x38f4f3B6533de0023b9DCd04b02F93d36ad1F9f9' as Address, + chainId: unichain.id, + name: 'Gauntlet USDC', + asset: USDC, + lendProvider: 'morpho', + } + + export const MetaMorphoUSDC: LendMarketConfig = { + address: '0x99067e5D73b1d6F1b5856E59209e12F5a0f86DED' as Address, + chainId: baseSepolia.id, + name: 'MetaMorpho USDC Vault (Base Sepolia)', + asset: USDC, + lendProvider: 'morpho', + } + + export const USDCDemoVault: LendMarketConfig = { + address: '0x297E324C46309E93112610ebf35559685b4E3547' as Address, + chainId: baseSepolia.id, + name: 'USDC Demo Vault (Base Sepolia)', + asset: USDC_DEMO, + lendProvider: 'morpho', + } + + +export function createVerbsConfig(): ReactVerbsConfig<'turnkey'> { + return { + wallet: { + hostedWalletConfig: { + provider: { + type: 'turnkey', + }, + }, + smartWalletConfig: { + provider: { + type: 'default', + // converts to '0xee4a2159c53ceed04edf4ce23cc97c5c' + attributionSuffix: 'verbs', + }, + }, + }, + lend: { + provider: 'morpho', + marketAllowlist: [GauntletUSDC, MetaMorphoUSDC, USDCDemoVault], + }, + chains: [ + { + chainId: unichain.id, + rpcUrls: env.VITE_UNICHAIN_RPC_URL ? [env.VITE_UNICHAIN_RPC_URL] : undefined, + bundler: { + type: 'pimlico', + url: env.VITE_UNICHAIN_BUNDLER_URL, + sponsorshipPolicyId: env.VITE_UNICHAIN_BUNDLER_SPONSORSHIP_POLICY, + }, + }, + { + chainId: baseSepolia.id, + rpcUrls: env.VITE_BASE_SEPOLIA_RPC_URL + ? [env.VITE_BASE_SEPOLIA_RPC_URL] + : undefined, + bundler: { + type: 'simple', + url: env.VITE_BASE_SEPOLIA_BUNDER_URL, + }, + }, + ], + } +} + +export function initializeVerbs() { + return createVerbs(createVerbsConfig()) +} + +export function getVerbs() { + return initializeVerbs() +} + +initializeVerbs() \ No newline at end of file diff --git a/packages/demo/frontend/src/envVars.ts b/packages/demo/frontend/src/envVars.ts index 2b8c4702..0a90eda6 100644 --- a/packages/demo/frontend/src/envVars.ts +++ b/packages/demo/frontend/src/envVars.ts @@ -18,6 +18,32 @@ const envVarSchema = z.object({ .string() .optional() .describe('Session signer ID for server-side signing'), + VITE_UNICHAIN_RPC_URL: z + .string() + .url() + .default('https://rpc.unicchain.com') + .describe('Unichain RPC URL for wallet connection'), + VITE_UNICHAIN_BUNDLER_URL: z + .string() + .url() + .default('https://bundler.unicchain.com') + .describe('Unichain Bundler URL for wallet connection'), + VITE_UNICHAIN_BUNDLER_SPONSORSHIP_POLICY: z + .string() + .default('dummy-sponsorship-policy') + .describe('Unichain Bundler Sponsorship Policy for wallet connection'), + VITE_BASE_SEPOLIA_RPC_URL: z + .string() + .url() + .default('https://rpc.base.org') + .describe('Base Sepolia RPC URL for wallet connection'), + VITE_BASE_SEPOLIA_BUNDER_URL: z + .string() + .url() + .default('https://bundler.base.org') + .describe('Base Sepolia Bundler URL for wallet connection'), + VITE_TURNKEY_ORGANIZATION_ID: z.string(), + VITE_TURNKEY_AUTH_ID: z.string(), }) export const env = envVarSchema.parse(import.meta.env) diff --git a/packages/demo/frontend/src/index.css b/packages/demo/frontend/src/index.css index 1b0edc19..ecbbc20c 100644 --- a/packages/demo/frontend/src/index.css +++ b/packages/demo/frontend/src/index.css @@ -1,68 +1,128 @@ -@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap') +layer(base); -@tailwind base; -@tailwind components; -@tailwind utilities; +@import 'tailwindcss'; -@layer base { - * { - @apply border-terminal-border; +@source './**/*.{js,ts,jsx,tsx}'; + +@theme { + --font-mono: JetBrains Mono, Monaco, Menlo, Consolas, monospace; + + --color-terminal-bg: #1d2021; + --color-terminal-secondary: #282828; + --color-terminal-border: #504945; + --color-terminal-text: #ebdbb2; + --color-terminal-muted: #a89984; + --color-terminal-dim: #665c54; + --color-terminal-accent: #b8bb26; + --color-terminal-error: #fb4934; + --color-terminal-warning: #fabd2f; + --color-terminal-success: #b8bb26; + --color-terminal-info: #83a598; + + --animate-cursor-blink: blink 1s infinite; + --animate-glow: glow 2s ease-in-out infinite alternate; + + --shadow-terminal: 0 0 20px rgba(184, 187, 38, 0.3); + --shadow-terminal-inner: inset 0 0 20px rgba(184, 187, 38, 0.1); + + @keyframes blink { + 0%, + 50% { + opacity: 1; + } + 51%, + 100% { + opacity: 0; + } } - body { - @apply bg-terminal-bg text-terminal-text font-mono; - @apply m-0 p-0; - @apply selection:bg-terminal-muted selection:text-terminal-bg; + @keyframes glow { + 0% { + text-shadow: 0 0 5px #b8bb26; + } + 100% { + text-shadow: + 0 0 20px #b8bb26, + 0 0 30px #b8bb26; + } } +} - html, - body, - #root { - @apply w-full h-full; +/* + The default border color has changed to `currentcolor` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. +*/ +@layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentcolor); } } -@layer components { - .terminal-glow { - text-shadow: 0 0 5px currentColor; - } +@utility terminal-glow { + text-shadow: 0 0 5px currentColor; +} - .terminal-cursor { - @apply inline-block w-2 h-5 bg-terminal-text animate-cursor-blink; - } +@utility terminal-cursor { + @apply inline-block w-2 h-5 bg-terminal-text animate-cursor-blink; +} - .terminal-line { - @apply flex items-center min-h-[1.5rem] leading-relaxed; - } +@utility terminal-line { + @apply flex items-center min-h-6 leading-relaxed; +} - .terminal-prompt { - @apply text-terminal-muted mr-2 flex-shrink-0; - } +@utility terminal-prompt { + @apply text-terminal-muted mr-2 flex-shrink-0; +} - .terminal-input { - @apply bg-transparent outline-none flex-1 text-terminal-text caret-transparent; - } +@utility terminal-input { + @apply bg-transparent outline-hidden flex-1 text-terminal-text caret-transparent; +} - .terminal-output { - @apply text-terminal-text whitespace-pre-wrap; - } +@utility terminal-output { + @apply text-terminal-text whitespace-pre-wrap; +} - .terminal-error { - @apply text-terminal-error; - } +@utility terminal-error { + @apply text-terminal-error; +} - .terminal-success { - @apply text-terminal-success; - } +@utility terminal-success { + @apply text-terminal-success; +} - .terminal-warning { - @apply text-terminal-warning; - } +@utility terminal-warning { + @apply text-terminal-warning; +} + +@utility terminal-info { + @apply text-terminal-info; +} + +@utility terminal-accent { + @apply text-terminal-accent; +} - .terminal-info { - @apply text-terminal-info; +@layer base { + * { + @apply border-terminal-border; + } + body { + @apply bg-terminal-bg text-terminal-text font-mono; + @apply m-0 p-0; + @apply selection:bg-terminal-muted selection:text-terminal-bg; } - .terminal-accent { - @apply text-terminal-accent; + html, + body, + #root { + @apply w-full h-full; } } diff --git a/packages/demo/frontend/src/main.tsx b/packages/demo/frontend/src/main.tsx index bef5202a..7182ea74 100644 --- a/packages/demo/frontend/src/main.tsx +++ b/packages/demo/frontend/src/main.tsx @@ -1,5 +1,6 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' +import "@turnkey/react-wallet-kit/styles.css"; import './index.css' import App from './App.tsx' diff --git a/packages/demo/frontend/src/providers/TurnkeyProvider.tsx b/packages/demo/frontend/src/providers/TurnkeyProvider.tsx new file mode 100644 index 00000000..4ad82bdb --- /dev/null +++ b/packages/demo/frontend/src/providers/TurnkeyProvider.tsx @@ -0,0 +1,22 @@ +import { + TurnkeyProvider, + type TurnkeyProviderConfig, + } from "@turnkey/react-wallet-kit"; +import { env } from "../envVars"; + + const turnkeyConfig: TurnkeyProviderConfig = { + organizationId: env.VITE_TURNKEY_ORGANIZATION_ID, + authProxyConfigId: env.VITE_TURNKEY_AUTH_ID, + // ui: { + // supressMissingStylesError: true, // Disable styles check. Only do this if you are sure that the styles are being applied correctly. + // }, + }; + + export function Turnkey({ children }: { children: React.ReactNode }) { + return console.error("Turnkey error:", error), + }} + >{children}; + } \ No newline at end of file diff --git a/packages/demo/frontend/tailwind.config.js b/packages/demo/frontend/tailwind.config.js deleted file mode 100644 index 29153efa..00000000 --- a/packages/demo/frontend/tailwind.config.js +++ /dev/null @@ -1,45 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -export default { - content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], - theme: { - extend: { - fontFamily: { - mono: ['JetBrains Mono', 'Monaco', 'Menlo', 'Consolas', 'monospace'], - }, - colors: { - terminal: { - bg: '#1d2021', // Gruvbox dark background - secondary: '#282828', // Gruvbox dark0_soft - border: '#504945', // Gruvbox dark2 - text: '#ebdbb2', // Gruvbox light0 - muted: '#a89984', // Gruvbox light4 - dim: '#665c54', // Gruvbox dark3 - accent: '#b8bb26', // Gruvbox bright_green - error: '#fb4934', // Gruvbox bright_red - warning: '#fabd2f', // Gruvbox bright_yellow - success: '#b8bb26', // Gruvbox bright_green - info: '#83a598', // Gruvbox bright_blue - }, - }, - animation: { - 'cursor-blink': 'blink 1s infinite', - glow: 'glow 2s ease-in-out infinite alternate', - }, - keyframes: { - blink: { - '0%, 50%': { opacity: '1' }, - '51%, 100%': { opacity: '0' }, - }, - glow: { - '0%': { 'text-shadow': '0 0 5px #b8bb26' }, - '100%': { 'text-shadow': '0 0 20px #b8bb26, 0 0 30px #b8bb26' }, - }, - }, - boxShadow: { - terminal: '0 0 20px rgba(184, 187, 38, 0.3)', - 'terminal-inner': 'inset 0 0 20px rgba(184, 187, 38, 0.1)', - }, - }, - }, - plugins: [], -} diff --git a/packages/demo/frontend/vite.config.ts b/packages/demo/frontend/vite.config.ts index c996651c..d0fb2955 100644 --- a/packages/demo/frontend/vite.config.ts +++ b/packages/demo/frontend/vite.config.ts @@ -5,6 +5,7 @@ import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], optimizeDeps: { + include: ['eventemitter3'], exclude: ['@base-org/account'], }, }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e1635580..483b01e2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -119,7 +119,7 @@ importers: version: 6.21.3 vitest: specifier: ^1.6.1 - version: 1.6.1(@types/node@20.19.9)(jsdom@23.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 1.6.1(@types/node@20.19.9)(jsdom@23.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(lightningcss@1.30.1) packages/demo/contracts: dependencies: @@ -145,9 +145,15 @@ importers: '@privy-io/react-auth': specifier: ^2.24.0 version: 2.24.0(@solana/spl-token@0.4.12(@solana/web3.js@1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10))(@solana/web3.js@1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10))(@types/react@18.3.23)(bs58@6.0.0)(bufferutil@4.0.9)(permissionless@0.2.54(ox@0.9.3(typescript@5.8.3)(zod@4.0.5))(viem@2.33.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.0.5)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3)(use-sync-external-store@1.5.0(react@18.3.1))(utf-8-validate@5.0.10)(zod@4.0.5) + '@turnkey/react-wallet-kit': + specifier: ^1.1.2 + version: 1.1.2(@types/react@18.3.23)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.0.5) buffer: specifier: ^6.0.3 version: 6.0.3 + eventemitter3: + specifier: ^5 + version: 5.0.1 react: specifier: ^18 version: 18.3.1 @@ -167,6 +173,9 @@ importers: specifier: ^4.0.5 version: 4.0.5 devDependencies: + '@tailwindcss/postcss': + specifier: ^4.1.13 + version: 4.1.13 '@testing-library/jest-dom': specifier: ^6.1.4 version: 6.6.3 @@ -184,7 +193,7 @@ importers: version: 18.3.7(@types/react@18.3.23) '@vitejs/plugin-react': specifier: ^4.0.3 - version: 4.7.0(vite@4.5.14(@types/node@20.19.9)) + version: 4.7.0(vite@4.5.14(@types/node@20.19.9)(lightningcss@1.30.1)) autoprefixer: specifier: ^10.4.16 version: 10.4.21(postcss@8.5.6) @@ -206,9 +215,12 @@ importers: postcss: specifier: ^8.4.31 version: 8.5.6 + postcss-import: + specifier: ^16.1.1 + version: 16.1.1(postcss@8.5.6) tailwindcss: - specifier: ^3.3.3 - version: 3.4.17 + specifier: ^4.1.13 + version: 4.1.13 typescript: specifier: ^5.2.2 version: 5.8.3 @@ -217,10 +229,10 @@ importers: version: 8.38.0(eslint@9.31.0(jiti@1.21.7))(typescript@5.8.3) vite: specifier: ^4.4.5 - version: 4.5.14(@types/node@20.19.9) + version: 4.5.14(@types/node@20.19.9)(lightningcss@1.30.1) vitest: specifier: ^1.6.1 - version: 1.6.1(@types/node@20.19.9)(jsdom@23.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 1.6.1(@types/node@20.19.9)(jsdom@23.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(lightningcss@1.30.1) packages/sdk: dependencies: @@ -293,7 +305,7 @@ importers: version: 5.8.3 vitest: specifier: ^1.6.1 - version: 1.6.1(@types/node@18.19.121)(jsdom@23.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 1.6.1(@types/node@18.19.121)(jsdom@23.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(lightningcss@1.30.1) packages: @@ -1852,9 +1864,9 @@ packages: cpu: [x64] os: [win32] - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} '@jest/diff-sequences@30.0.1': resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} @@ -1875,6 +1887,9 @@ packages: '@jridgewell/gen-mapping@0.3.12': resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -1882,6 +1897,9 @@ packages: '@jridgewell/sourcemap-codec@1.5.4': resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.29': resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} @@ -2170,10 +2188,6 @@ packages: '@phosphor-icons/webcomponents@2.1.5': resolution: {integrity: sha512-JcvQkZxvcX2jK+QCclm8+e8HXqtdFW9xV4/kk2aL9Y3dJA2oQVt+pzbv1orkumz3rfx4K9mn9fDoMr1He1yr7Q==} - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - '@pnpm/config.env-replace@1.1.0': resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} engines: {node: '>=12.22.0'} @@ -2635,6 +2649,94 @@ packages: resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} engines: {node: '>=14.16'} + '@tailwindcss/node@4.1.13': + resolution: {integrity: sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==} + + '@tailwindcss/oxide-android-arm64@4.1.13': + resolution: {integrity: sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.13': + resolution: {integrity: sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.13': + resolution: {integrity: sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.13': + resolution: {integrity: sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': + resolution: {integrity: sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': + resolution: {integrity: sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.13': + resolution: {integrity: sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.13': + resolution: {integrity: sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.13': + resolution: {integrity: sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.13': + resolution: {integrity: sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': + resolution: {integrity: sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.13': + resolution: {integrity: sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.13': + resolution: {integrity: sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==} + engines: {node: '>= 10'} + + '@tailwindcss/postcss@4.1.13': + resolution: {integrity: sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==} + '@tanstack/react-virtual@3.13.12': resolution: {integrity: sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==} peerDependencies: @@ -3192,10 +3294,6 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} - engines: {node: '>=12'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -3204,17 +3302,10 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - antlr4@4.13.2: resolution: {integrity: sha512-QiVbZhyy4xAZ17UPEuG3YTOt8ZaoeOR1CvEAqrEsDBsOqINslaB147i9xqljZqoyf5S+EUlGStaj+t22LT9MOg==} engines: {node: '>=16'} - any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -3223,9 +3314,6 @@ packages: resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} engines: {node: '>=14'} - arg@5.0.2: - resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -3376,10 +3464,6 @@ packages: bignumber.js@9.3.1: resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} @@ -3478,10 +3562,6 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - camelcase-css@2.0.1: - resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} - engines: {node: '>= 6'} - camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} @@ -3518,14 +3598,14 @@ packages: check-error@1.0.3: resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -3600,10 +3680,6 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} - commander@8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} @@ -3689,11 +3765,6 @@ packages: css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - cssstyle@4.6.0: resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} engines: {node: '>=18'} @@ -3833,9 +3904,6 @@ packages: engines: {node: '>= 4.0.0'} hasBin: true - didyoumean@1.2.2: - resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3843,9 +3911,6 @@ packages: dijkstrajs@1.0.3: resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} - dlv@1.1.3: - resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -3875,9 +3940,6 @@ packages: duplexify@4.1.3: resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - eciesjs@0.4.15: resolution: {integrity: sha512-r6kEJXDKecVOCj2nLMuXK/FCPeurW33+3JRpfXVbjLja3XUYFfD9I/JBreH6sUyzcm3G/YQboBjMla6poKeSdA==} engines: {bun: '>=1', deno: '>=2', node: '>=16'} @@ -3896,9 +3958,6 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - encode-utf8@1.0.3: resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} @@ -3915,6 +3974,10 @@ packages: resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} engines: {node: '>=10.0.0'} + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + enquirer@2.3.6: resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} engines: {node: '>=8.6'} @@ -4268,10 +4331,6 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - form-data-encoder@2.1.4: resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} engines: {node: '>= 14.17'} @@ -4349,10 +4408,6 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true - glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} @@ -4543,10 +4598,6 @@ packages: resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} engines: {node: '>= 0.4'} - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - is-boolean-object@1.2.2: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} @@ -4702,9 +4753,6 @@ packages: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jake@10.9.2: resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} engines: {node: '>=10'} @@ -4723,6 +4771,10 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true + jiti@2.6.0: + resolution: {integrity: sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ==} + hasBin: true + jose@4.15.9: resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} @@ -4829,9 +4881,69 @@ packages: libphonenumber-js@1.12.10: resolution: {integrity: sha512-E91vHJD61jekHHR/RF/E83T/CMoaLXT7cwYA75T4gim4FZjnM6hbJjVIGg7chqlSqRsSvQ3izGmOjHy1SQzcGQ==} - lilconfig@3.1.3: - resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} - engines: {node: '>=14'} + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + engines: {node: '>= 12.0.0'} lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -4918,6 +5030,9 @@ packages: magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -5002,6 +5117,10 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + mipd@0.0.7: resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==} peerDependencies: @@ -5024,9 +5143,6 @@ packages: multiformats@9.9.0: resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} - mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -5106,10 +5222,6 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-hash@3.0.0: - resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} - engines: {node: '>= 6'} - object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -5265,9 +5377,6 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - package-json@8.1.1: resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} engines: {node: '>=14.16'} @@ -5304,10 +5413,6 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -5376,10 +5481,6 @@ packages: resolution: {integrity: sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==} hasBin: true - pirates@4.0.7: - resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} - engines: {node: '>= 6'} - pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -5402,40 +5503,12 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} - postcss-import@15.1.0: - resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} - engines: {node: '>=14.0.0'} + postcss-import@16.1.1: + resolution: {integrity: sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ==} + engines: {node: '>=18.0.0'} peerDependencies: postcss: ^8.0.0 - postcss-js@4.0.1: - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} - engines: {node: ^12 || ^14 || >= 16} - peerDependencies: - postcss: ^8.4.21 - - postcss-load-config@4.0.2: - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true - - postcss-nested@6.2.0: - resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.2.14 - - postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} - engines: {node: '>=4'} - postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} @@ -5628,10 +5701,6 @@ packages: resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -5966,10 +6035,6 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - string.prototype.matchall@4.0.12: resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} @@ -5999,10 +6064,6 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -6043,11 +6104,6 @@ packages: stylis@4.3.6: resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} - sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true - supersim@0.1.0-alpha.58: resolution: {integrity: sha512-8YHo3j5AvhwDQNWuQvGnN12DBOV1pZYNl2XNM1IMgLydhe3oj9eaWay4F/ZSaSUOJiNPdAv9kDijepXeEAh+6A==} engines: {node: '>=18.0.0'} @@ -6085,15 +6141,21 @@ packages: resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} engines: {node: '>=10.0.0'} - tailwindcss@3.4.17: - resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} - engines: {node: '>=14.0.0'} - hasBin: true + tailwindcss@4.1.13: + resolution: {integrity: sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==} + + tapable@2.2.3: + resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==} + engines: {node: '>=6'} tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} + tar@7.5.1: + resolution: {integrity: sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==} + engines: {node: '>=18'} + tdigest@0.1.2: resolution: {integrity: sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==} @@ -6107,13 +6169,6 @@ packages: text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} - - thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - thread-stream@0.15.2: resolution: {integrity: sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==} @@ -6181,9 +6236,6 @@ packages: ts-case-convert@2.1.0: resolution: {integrity: sha512-Ye79el/pHYXfoew6kqhMwCoxp4NWjKNcm2kBzpmEMIU9dd9aBmHNNFtZ+WTm0rz1ngyDmfqDXDlyUnBXayiD0w==} - ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -6658,10 +6710,6 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -6746,6 +6794,10 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} @@ -8683,14 +8735,9 @@ snapshots: '@img/sharp-win32-x64@0.33.5': optional: true - '@isaacs/cliui@8.0.2': + '@isaacs/fs-minipass@4.0.1': dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 + minipass: 7.1.2 '@jest/diff-sequences@30.0.1': {} @@ -8709,10 +8756,17 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.4 '@jridgewell/trace-mapping': 0.3.29 + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.4': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.29': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -9139,9 +9193,6 @@ snapshots: dependencies: lit: 3.3.0 - '@pkgjs/parseargs@0.11.0': - optional: true - '@pnpm/config.env-replace@1.1.0': {} '@pnpm/network.ca-file@1.0.2': @@ -10390,6 +10441,78 @@ snapshots: dependencies: defer-to-connect: 2.0.1 + '@tailwindcss/node@4.1.13': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.6.0 + lightningcss: 1.30.1 + magic-string: 0.30.19 + source-map-js: 1.2.1 + tailwindcss: 4.1.13 + + '@tailwindcss/oxide-android-arm64@4.1.13': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.13': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.13': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.13': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.13': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.13': + optional: true + + '@tailwindcss/oxide@4.1.13': + dependencies: + detect-libc: 2.1.0 + tar: 7.5.1 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.13 + '@tailwindcss/oxide-darwin-arm64': 4.1.13 + '@tailwindcss/oxide-darwin-x64': 4.1.13 + '@tailwindcss/oxide-freebsd-x64': 4.1.13 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.13 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.13 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.13 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.13 + '@tailwindcss/oxide-linux-x64-musl': 4.1.13 + '@tailwindcss/oxide-wasm32-wasi': 4.1.13 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.13 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.13 + + '@tailwindcss/postcss@4.1.13': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.1.13 + '@tailwindcss/oxide': 4.1.13 + postcss: 8.5.6 + tailwindcss: 4.1.13 + '@tanstack/react-virtual@3.13.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@tanstack/virtual-core': 3.13.12 @@ -10948,7 +11071,7 @@ snapshots: '@typescript-eslint/types': 8.38.0 eslint-visitor-keys: 4.2.1 - '@vitejs/plugin-react@4.7.0(vite@4.5.14(@types/node@20.19.9))': + '@vitejs/plugin-react@4.7.0(vite@4.5.14(@types/node@20.19.9)(lightningcss@1.30.1))': dependencies: '@babel/core': 7.28.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.0) @@ -10956,7 +11079,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 4.5.14(@types/node@20.19.9) + vite: 4.5.14(@types/node@20.19.9)(lightningcss@1.30.1) transitivePeerDependencies: - supports-color @@ -12027,20 +12150,14 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.1.0: {} - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansi-styles@5.2.0: {} - ansi-styles@6.2.1: {} - antlr4@4.13.2: {} - any-promise@1.3.0: {} - anymatch@3.1.3: dependencies: normalize-path: 3.0.0 @@ -12048,8 +12165,6 @@ snapshots: are-docs-informative@0.0.2: {} - arg@5.0.2: {} - argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -12245,8 +12360,6 @@ snapshots: bignumber.js@9.3.1: {} - binary-extensions@2.3.0: {} - bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 @@ -12363,8 +12476,6 @@ snapshots: callsites@3.1.0: {} - camelcase-css@2.0.1: {} - camelcase@5.3.1: {} camelize@1.0.1: {} @@ -12401,22 +12512,12 @@ snapshots: dependencies: get-func-name: 2.0.2 - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - chokidar@4.0.3: dependencies: readdirp: 4.1.2 + chownr@3.0.0: {} + cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 @@ -12480,8 +12581,6 @@ snapshots: commander@2.20.3: {} - commander@4.1.1: {} - commander@8.3.0: {} commander@9.5.0: {} @@ -12567,8 +12666,6 @@ snapshots: css.escape@1.5.1: {} - cssesc@3.0.0: {} - cssstyle@4.6.0: dependencies: '@asamuzakjp/css-color': 3.2.0 @@ -12701,14 +12798,10 @@ snapshots: transitivePeerDependencies: - supports-color - didyoumean@1.2.2: {} - diff-sequences@29.6.3: {} dijkstrajs@1.0.3: {} - dlv@1.1.3: {} - doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -12738,8 +12831,6 @@ snapshots: readable-stream: 3.6.2 stream-shift: 1.0.3 - eastasianwidth@0.2.0: {} - eciesjs@0.4.15: dependencies: '@ecies/ciphers': 0.2.4(@noble/ciphers@1.3.0) @@ -12765,8 +12856,6 @@ snapshots: emoji-regex@8.0.0: {} - emoji-regex@9.2.2: {} - encode-utf8@1.0.3: {} encoding@0.1.13: @@ -12791,6 +12880,11 @@ snapshots: engine.io-parser@5.2.3: {} + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.10 + tapable: 2.2.3 + enquirer@2.3.6: dependencies: ansi-colors: 4.1.3 @@ -13331,11 +13425,6 @@ snapshots: dependencies: is-callable: 1.2.7 - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - form-data-encoder@2.1.4: {} form-data@4.0.4: @@ -13418,15 +13507,6 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.4.5: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - glob@8.1.0: dependencies: fs.realpath: 1.0.0 @@ -13637,10 +13717,6 @@ snapshots: dependencies: has-bigints: 1.1.0 - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - is-boolean-object@1.2.2: dependencies: call-bound: 1.0.4 @@ -13783,12 +13859,6 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - jake@10.9.2: dependencies: async: 3.2.6 @@ -13821,7 +13891,10 @@ snapshots: chalk: 4.1.2 pretty-format: 30.0.2 - jiti@1.21.7: {} + jiti@1.21.7: + optional: true + + jiti@2.6.0: {} jose@4.15.9: {} @@ -13924,7 +13997,50 @@ snapshots: libphonenumber-js@1.12.10: {} - lilconfig@3.1.3: {} + lightningcss-darwin-arm64@1.30.1: + optional: true + + lightningcss-darwin-x64@1.30.1: + optional: true + + lightningcss-freebsd-x64@1.30.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.1: + optional: true + + lightningcss-linux-arm64-gnu@1.30.1: + optional: true + + lightningcss-linux-arm64-musl@1.30.1: + optional: true + + lightningcss-linux-x64-gnu@1.30.1: + optional: true + + lightningcss-linux-x64-musl@1.30.1: + optional: true + + lightningcss-win32-arm64-msvc@1.30.1: + optional: true + + lightningcss-win32-x64-msvc@1.30.1: + optional: true + + lightningcss@1.30.1: + dependencies: + detect-libc: 2.1.0 + optionalDependencies: + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 lines-and-columns@1.2.4: {} @@ -14006,6 +14122,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.4 + magic-string@0.30.19: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + math-intrinsics@1.1.0: {} md5@2.3.0: @@ -14069,6 +14189,10 @@ snapshots: minipass@7.1.2: {} + minizlib@3.1.0: + dependencies: + minipass: 7.1.2 + mipd@0.0.7(typescript@5.8.3): optionalDependencies: typescript: 5.8.3 @@ -14086,12 +14210,6 @@ snapshots: multiformats@9.9.0: {} - mz@2.7.0: - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 - nanoid@3.3.11: {} natural-compare@1.4.0: {} @@ -14194,8 +14312,6 @@ snapshots: object-assign@4.1.1: {} - object-hash@3.0.0: {} - object-inspect@1.13.4: {} object-is@1.1.6: @@ -14445,8 +14561,6 @@ snapshots: p-try@2.2.0: {} - package-json-from-dist@1.0.1: {} - package-json@8.1.1: dependencies: got: 12.6.1 @@ -14483,11 +14597,6 @@ snapshots: path-parse@1.0.7: {} - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 - path-type@4.0.0: {} pathe@1.1.2: {} @@ -14589,8 +14698,6 @@ snapshots: sonic-boom: 4.2.0 thread-stream: 3.1.0 - pirates@4.0.7: {} - pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -14607,35 +14714,13 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-import@15.1.0(postcss@8.5.6): + postcss-import@16.1.1(postcss@8.5.6): dependencies: postcss: 8.5.6 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.10 - postcss-js@4.0.1(postcss@8.5.6): - dependencies: - camelcase-css: 2.0.1 - postcss: 8.5.6 - - postcss-load-config@4.0.2(postcss@8.5.6): - dependencies: - lilconfig: 3.1.3 - yaml: 2.8.0 - optionalDependencies: - postcss: 8.5.6 - - postcss-nested@6.2.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-selector-parser: 6.1.2 - - postcss-selector-parser@6.1.2: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - postcss-value-parser@4.2.0: {} postcss@8.4.49: @@ -14830,10 +14915,6 @@ snapshots: process: 0.11.10 string_decoder: 1.3.0 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - readdirp@4.1.2: {} real-require@0.1.0: {} @@ -15260,12 +15341,6 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 - string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 @@ -15322,10 +15397,6 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: - dependencies: - ansi-regex: 6.1.0 - strip-bom@3.0.0: {} strip-final-newline@3.0.0: {} @@ -15364,16 +15435,6 @@ snapshots: stylis@4.3.6: {} - sucrase@3.35.0: - dependencies: - '@jridgewell/gen-mapping': 0.3.12 - commander: 4.1.1 - glob: 10.4.5 - lines-and-columns: 1.2.4 - mz: 2.7.0 - pirates: 4.0.7 - ts-interface-checker: 0.1.13 - supersim@0.1.0-alpha.58: dependencies: follow-redirects: 1.15.9 @@ -15421,32 +15482,9 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - tailwindcss@3.4.17: - dependencies: - '@alloc/quick-lru': 5.2.0 - arg: 5.0.2 - chokidar: 3.6.0 - didyoumean: 1.2.2 - dlv: 1.1.3 - fast-glob: 3.3.2 - glob-parent: 6.0.2 - is-glob: 4.0.3 - jiti: 1.21.7 - lilconfig: 3.1.3 - micromatch: 4.0.8 - normalize-path: 3.0.0 - object-hash: 3.0.0 - picocolors: 1.1.1 - postcss: 8.5.6 - postcss-import: 15.1.0(postcss@8.5.6) - postcss-js: 4.0.1(postcss@8.5.6) - postcss-load-config: 4.0.2(postcss@8.5.6) - postcss-nested: 6.2.0(postcss@8.5.6) - postcss-selector-parser: 6.1.2 - resolve: 1.22.10 - sucrase: 3.35.0 - transitivePeerDependencies: - - ts-node + tailwindcss@4.1.13: {} + + tapable@2.2.3: {} tar-stream@2.2.0: dependencies: @@ -15456,6 +15494,14 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + tar@7.5.1: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.1.0 + yallist: 5.0.0 + tdigest@0.1.2: dependencies: bintrees: 1.0.2 @@ -15466,14 +15512,6 @@ snapshots: text-table@0.2.0: {} - thenify-all@1.6.0: - dependencies: - thenify: 3.3.1 - - thenify@3.3.1: - dependencies: - any-promise: 1.3.0 - thread-stream@0.15.2: dependencies: real-require: 0.1.0 @@ -15532,8 +15570,6 @@ snapshots: ts-case-convert@2.1.0: {} - ts-interface-checker@0.1.13: {} - tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -15859,13 +15895,13 @@ snapshots: - utf-8-validate - zod - vite-node@1.6.1(@types/node@18.19.121): + vite-node@1.6.1(@types/node@18.19.121)(lightningcss@1.30.1): dependencies: cac: 6.7.14 debug: 4.4.1 pathe: 1.1.2 picocolors: 1.1.1 - vite: 5.4.19(@types/node@18.19.121) + vite: 5.4.19(@types/node@18.19.121)(lightningcss@1.30.1) transitivePeerDependencies: - '@types/node' - less @@ -15877,13 +15913,13 @@ snapshots: - supports-color - terser - vite-node@1.6.1(@types/node@20.19.9): + vite-node@1.6.1(@types/node@20.19.9)(lightningcss@1.30.1): dependencies: cac: 6.7.14 debug: 4.4.1 pathe: 1.1.2 picocolors: 1.1.1 - vite: 5.4.19(@types/node@20.19.9) + vite: 5.4.19(@types/node@20.19.9)(lightningcss@1.30.1) transitivePeerDependencies: - '@types/node' - less @@ -15895,7 +15931,7 @@ snapshots: - supports-color - terser - vite@4.5.14(@types/node@20.19.9): + vite@4.5.14(@types/node@20.19.9)(lightningcss@1.30.1): dependencies: esbuild: 0.18.20 postcss: 8.5.6 @@ -15903,8 +15939,9 @@ snapshots: optionalDependencies: '@types/node': 20.19.9 fsevents: 2.3.3 + lightningcss: 1.30.1 - vite@5.4.19(@types/node@18.19.121): + vite@5.4.19(@types/node@18.19.121)(lightningcss@1.30.1): dependencies: esbuild: 0.21.5 postcss: 8.5.6 @@ -15912,8 +15949,9 @@ snapshots: optionalDependencies: '@types/node': 18.19.121 fsevents: 2.3.3 + lightningcss: 1.30.1 - vite@5.4.19(@types/node@20.19.9): + vite@5.4.19(@types/node@20.19.9)(lightningcss@1.30.1): dependencies: esbuild: 0.21.5 postcss: 8.5.6 @@ -15921,8 +15959,9 @@ snapshots: optionalDependencies: '@types/node': 20.19.9 fsevents: 2.3.3 + lightningcss: 1.30.1 - vitest@1.6.1(@types/node@18.19.121)(jsdom@23.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + vitest@1.6.1(@types/node@18.19.121)(jsdom@23.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(lightningcss@1.30.1): dependencies: '@vitest/expect': 1.6.1 '@vitest/runner': 1.6.1 @@ -15941,8 +15980,8 @@ snapshots: strip-literal: 2.1.1 tinybench: 2.9.0 tinypool: 0.8.4 - vite: 5.4.19(@types/node@18.19.121) - vite-node: 1.6.1(@types/node@18.19.121) + vite: 5.4.19(@types/node@18.19.121)(lightningcss@1.30.1) + vite-node: 1.6.1(@types/node@18.19.121)(lightningcss@1.30.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 18.19.121 @@ -15957,7 +15996,7 @@ snapshots: - supports-color - terser - vitest@1.6.1(@types/node@20.19.9)(jsdom@23.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + vitest@1.6.1(@types/node@20.19.9)(jsdom@23.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(lightningcss@1.30.1): dependencies: '@vitest/expect': 1.6.1 '@vitest/runner': 1.6.1 @@ -15976,8 +16015,8 @@ snapshots: strip-literal: 2.1.1 tinybench: 2.9.0 tinypool: 0.8.4 - vite: 5.4.19(@types/node@20.19.9) - vite-node: 1.6.1(@types/node@20.19.9) + vite: 5.4.19(@types/node@20.19.9)(lightningcss@1.30.1) + vite-node: 1.6.1(@types/node@20.19.9)(lightningcss@1.30.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.19.9 @@ -16098,12 +16137,6 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - wrappy@1.0.2: {} ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): @@ -16143,6 +16176,8 @@ snapshots: yallist@3.1.1: {} + yallist@5.0.0: {} + yaml@1.10.2: {} yaml@2.8.0: {}