diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a03df86 --- /dev/null +++ b/.env.example @@ -0,0 +1,40 @@ +# --- Hathor fullnode (read-only) -------------------------------------------- +FULLNODE_URL=https://node1.testnet.hathor.network +HATHOR_NETWORK=testnet + +# --- Resource server -------------------------------------------------------- +RESOURCE_SERVER_PORT=3000 +RESOURCE_SERVER_PUBLIC_URL=http://localhost:3000 + +# Seller's static receiving address. The resource server emits this in 402's +# `payTo`. The seller controls this address (in any wallet of their choosing). +SELLER_ADDRESS= + +# --- Server secret (HMACs the requestId) ------------------------------------ +# Must be at least 32 bytes of entropy in prod. Local dev: any non-empty string. +SERVER_SECRET=change-me-32-bytes-min +REQUEST_ID_TTL_SECONDS=120 + +# --- Dedup + blocklist store ------------------------------------------------ +DEDUP_DB_PATH=./data/payments.sqlite + +# Above this amount, the verifier refuses zero-conf (requires meta.first_block). +# Default: never require a block (zero-conf for everything — fine for POC). +ZERO_CONF_MAX_AMOUNT=9007199254740991 + +# --- Pricing (atomic units; 1 HTR = 100) ------------------------------------ +HTR_PAYMENT_AMOUNT=100 +GENERATE_MAX_PRICE=500 + +# --- Verify path: 'self' or 'facilitator' ----------------------------------- +VERIFY_MODE=self +FACILITATOR_PORT=8402 +FACILITATOR_URL=http://localhost:8402 + +# --- Seller's wallet-headless (only needed for the upto refund) ------------ +SELLER_WALLET_URL=http://wallet-headless:8000 +SELLER_WALLET_ID=seller +SELLER_SEED= + +# --- Optional: only needed if you want to run the CLI client.js ------------- +BUYER_SEED= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..484ef6e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +*.log +.env +.deploy +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d6c7592 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +FROM node:22-alpine + +# `curl` for the init script + container healthchecks; build deps installed +# transiently for the `better-sqlite3` native compile. +RUN apk add --no-cache curl + +WORKDIR /app + +COPY package.json package-lock.json* ./ + +# Install with build deps, then drop them to keep the image small. +RUN apk add --no-cache --virtual .build python3 make g++ \ + && npm install --omit=dev \ + && apk del .build + +COPY *.js ./ +COPY *.sh ./ + +# Persistent dedup + blocklist SQLite file lives here. +RUN mkdir -p /app/data +VOLUME ["/app/data"] + +EXPOSE 8402 3000 + +# Default: run the resource server. Override CMD for the facilitator service. +CMD ["node", "resource-server.js"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..231ccf2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Hathor Network + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 790c76a..0a9c7fa 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,167 @@ -# x402 Payment Protocol POC for Hathor Network +# x402 Payment Protocol PoC for Hathor Network -> Machines paying machines. HTTP 402 meets nano contract escrow. +> Machines paying machines, settled on Hathor with regular UTXO payments. +> No nano contracts, no escrow blueprint, no facilitator hot wallet. -See the [x402 RFC](https://github.com/HathorNetwork/rfcs/blob/feat/x402-support/projects/x402/0001-x402-support.md) for the full protocol design. +This is a proof-of-concept implementation of the [x402 protocol](https://www.x402.org/) +on [Hathor Network](https://hathor.network/) using the **`hathor-direct`** scheme: the +client makes an ordinary "send tokens" transaction to the seller, signs a server-issued +challenge with its payer key, and the server verifies the on-chain payment + signature +read-only. + +See the full design rationale (with the rejected nano-contract design and the +fair-exchange / trust analysis) in +[`../x402-hathor-no-nc-proposal.md`](../x402-hathor-no-nc-proposal.md). + +## What is x402? + +[x402](https://www.x402.org/) repurposes the HTTP `402 Payment Required` status code +for machine-to-machine payments: a client requests a paid resource, the server returns +a 402 with payment requirements, the client pays, retries the request with proof, and +gets the resource. The client is always code — an AI agent, a script, a backend +service. Never a human clicking buttons. + +## How Hathor does it (this PoC) + +``` +┌──────────────┐ ┌──────────────────────────┐ ┌─────────────────┐ +│ dApp / │──fetch──▶│ resource-server │──read───▶│ Hathor fullnode │ +│ agent │ │ verifier.js + │ │ (testnet/etc.) │ +│ │ │ dedupStore + │ └─────────────────┘ +│ htr_sendTx │ │ voidWatcher + │ ▲ +│ htr_signWith │ │ (refundIssuer for │ │ +│ Address │ │ the upto scheme) │ │ +└──────┬───────┘ └──────────┬───────────────┘ │ + │ │ POST /wallet/send-tx (refund only) + ▼ ▼ + Hathor wallet wallet-headless (seller refund wallet) + (desktop / mobile / + MetaMask snap) +``` + +Two on-chain transactions in the worst case: + +1. **Client → seller**: a plain `sendTransaction` paying `amount` to `payTo`. + This is the *only* tx for `hathor-direct` (exact). +2. **Seller → client** (upto only): a refund tx for `amount - chargedAmount`, + issued by the seller's wallet-headless after the route handler reports + actual usage. + +The server (or an optional facilitator wrapper) is purely read-only against the +fullnode: + +- Verify the on-chain payment (correct `payTo`, `amount`, asset, not voided). +- Verify the payer signature over the server-issued `requestId`. +- Atomically dedup against the SQLite ledger so the same payment can't be replayed. +- Kick off a void-watcher for zero-conf payments; double-spending payers get + blocklisted. + +## Schemes + +| Scheme | Semantics | Settlement | +|---|---|---| +| `hathor-direct` | Pay exact amount up front. | No-op — payment is final on-chain. | +| `hathor-direct-upto` | Authorize a max. Server charges actual usage. | Server issues a refund tx for `max - charged` from its own wallet. | + +Both use the same wire protocol and verifier code path; `upto` adds the optional +refund step. + +## Wire protocol + +402 body returned by the resource server: + +```jsonc +{ + "x402Version": 2, + "accepts": [{ + "scheme": "hathor-direct" | "hathor-direct-upto", + "network": "hathor:testnet", + "amount": "100", // atomic units; 1 HTR = 100. For upto, this is MAX. + "asset": "00", // "00" = HTR + "payTo": "WXf4x…", + "resource": "https://host/route", + "maxTimeoutSeconds": 120, + "description": "Pay 1.00 HTR", + "extra": { + "requestId": ".", + "facilitatorUrl": "https://…" // optional + } + }] +} +``` + +The client retries with a `PAYMENT-SIGNATURE` header (base64-encoded JSON): + +```jsonc +{ + "x402Version": 2, + "scheme": "hathor-direct", + "network": "hathor:testnet", + "payload": { + "txId": "000abc…", + "payerAddress": "WPo2…", + "signature": "BASE64…", + "requestId": "." + } +} +``` + +## Components + +| Component | File | Port | What it does | +|---|---|---|---| +| **Resource server** | `resource-server.js` | 3000 | Paid `/weather` (direct) + `/generate` (upto). Self-verifies via `verifier.js`. | +| **Facilitator** (optional) | `facilitator.js` | 8402 | Thin HTTP wrapper around the verifier. No wallet, no seeds, no nano contracts. | +| **Verifier** | `verifier.js` | — | Single source of truth for "is this payment claim valid?" | +| **Dedup store** | `dedupStore.js` + SQLite | — | Atomic `(txId, outputIndex)` ledger + payer blocklist. | +| **requestId** | `requestId.js` | — | Stateless HMAC challenge token (binds payment ↔ request). | +| **Void watcher** | `voidWatcher.js` | — | Background detection of double-spent payments → blocklist. | +| **Refund issuer** | `refundIssuer.js` | — | Calls the seller's wallet-headless to issue upto refunds. | +| **CLI client** | `client.js` | — | Buyer-side smoke test using `@hathor/wallet-lib`. | +| **dApp** | `dapp/` | 3000 | Browser client (Next.js + WalletConnect/Reown + MetaMask Snap). | +| **MCP example** | `examples/mcp-server/` | stdio | Paid MCP tool gated by `hathor-direct`. | + +## Quick start + +```bash +# 0. Fund a Hathor address on testnet (faucet) — that's the seller address. +# Generate a seed for the seller wallet (only needed for the upto scheme). + +cp .env.example .env +$EDITOR .env # set SELLER_ADDRESS, SELLER_SEED, SERVER_SECRET + +docker compose up --build +# -> wallet-headless on :8000 (seller wallet) +# -> resource-server on :3000 (paid /weather, /generate) +# -> dapp on :4020 + +# Browser: http://localhost:4020 — connect WalletConnect, fetch the paid URL. +# CLI: BUYER_SEED='...' node client.js --route weather +``` + +## What this PoC is NOT + +- **Not production-ready.** Idempotent retry response caching, blocklist + sharing, structured logging, metrics, key rotation for `SERVER_SECRET` + are all deferred. +- **Not confidential-transaction-enabled.** Hathor's shielded outputs are + alpha and out of mainnet — the design is *compatible* with future CT but + this PoC doesn't exercise it. +- **No protocol-level refund** beyond the upto remainder. If the server takes + the payment and doesn't deliver, there is no on-chain recovery — same trust + model as EVM `exact`. Reputation + small amounts + amount caps is the + bounding mechanism. + +## References + +- Design proposal: [`../x402-hathor-no-nc-proposal.md`](../x402-hathor-no-nc-proposal.md) +- x402 specification: +- x402 v1 → v2 migration: +- Hathor wallet-lib `signMessage`/`verifyMessage`: + `hathor-wallet-lib/src/utils/crypto.ts` +- Hathor RPC methods (`htr_sendTransaction`, `htr_signWithAddress`): + `hathor-rpc-lib/packages/hathor-rpc-handler/src/types/rpcRequest.ts` + +## License + +MIT diff --git a/client.js b/client.js new file mode 100644 index 0000000..8ca9a30 --- /dev/null +++ b/client.js @@ -0,0 +1,178 @@ +// x402 buyer CLI — non-browser smoke test for the hathor-direct flow. +// +// Uses @hathor/wallet-lib directly (no wallet-headless dependency) because: +// 1. We need wallet.signMessageWithAddress(), which the headless HTTP API +// doesn't expose today. +// 2. A single-process CLI is the simplest "agent making a paid request". +// +// Usage: +// BUYER_SEED='abandon abandon ...' node client.js [--route weather|generate] +// BUYER_SEED='...' node client.js --url http://localhost:3000/weather +// +// Requires the buyer wallet to be funded on the configured network (testnet +// faucet is sufficient). + +'use strict'; + +const config = require('./config'); +const { log } = require('./helpers'); + +const hathorLib = require('@hathor/wallet-lib'); +const { HathorWallet, Network } = hathorLib; + +// --- args ------------------------------------------------------------------- + +function parseArgs(argv) { + const out = { route: 'weather', url: null }; + for (let i = 2; i < argv.length; i++) { + const a = argv[i]; + if (a === '--route') out.route = argv[++i]; + else if (a.startsWith('--route=')) out.route = a.split('=')[1]; + else if (a === '--url') out.url = argv[++i]; + else if (a.startsWith('--url=')) out.url = a.split('=')[1]; + } + return out; +} + +const args = parseArgs(process.argv); +const url = args.url || `http://localhost:${config.resourceServerPort}/${args.route}`; + +if (!process.env.BUYER_SEED) { + log('CLIENT', 'BUYER_SEED is required. Export a 24-word seed.'); + process.exit(1); +} + +// --- wallet bootstrap ------------------------------------------------------- + +async function startWallet() { + log('CLIENT', `Starting wallet on ${config.network} (this can take a moment to sync)...`); + + const network = new Network(config.network); + const wallet = new HathorWallet({ + seed: process.env.BUYER_SEED, + server: `${config.fullnodeUrl}/v1a/`, + network, + pinCode: '000000', // local-only POC PIN + password: 'x402-poc', // protects in-memory key material + }); + + await wallet.start({ pinCode: '000000', password: 'x402-poc' }); + + // Wait until READY (state 3). The lib polls history; on a quiet wallet this + // is sub-second, on a heavily used one it can take longer. + const start = Date.now(); + while (true) { + const ready = wallet.isReady?.() ?? (wallet.state === 3); + if (ready) break; + if (Date.now() - start > 120_000) throw new Error('wallet failed to reach READY in 120s'); + await new Promise((r) => setTimeout(r, 500)); + } + + log('CLIENT', 'Wallet READY.'); + return wallet; +} + +// --- 402 + payment flow ----------------------------------------------------- + +async function fetch402() { + const resp = await fetch(url); + if (resp.status !== 402) { + throw new Error(`expected 402, got ${resp.status}`); + } + const body = await resp.json(); + if (!body.accepts || !body.accepts.length) throw new Error('no accepts in 402 body'); + return body.accepts[0]; +} + +async function pay(wallet, accept) { + const buyer = await wallet.getCurrentAddress({ markAsUsed: false }); + log('CLIENT', `Buyer address: ${buyer.address} (index ${buyer.index})`); + + log('CLIENT', `Paying ${(parseInt(accept.amount, 10) / 100).toFixed(2)} ${accept.asset === '00' ? 'HTR' : accept.asset} → ${accept.payTo}`); + + const sendTx = await wallet.sendManyOutputsSendTransaction( + [ + { + address: accept.payTo, + value: parseInt(accept.amount, 10), + token: accept.asset || '00', + }, + ], + { + changeAddress: buyer.address, + pinCode: '000000', + } + ); + const tx = await sendTx.run(); + const txId = tx?.hash || tx?.txId; + if (!txId) throw new Error('no txId returned from sendTransaction'); + log('CLIENT', `Broadcast tx ${txId}`); + + log('CLIENT', `Signing requestId with address index ${buyer.index} ...`); + const signature = await wallet.signMessageWithAddress( + accept.extra.requestId, + buyer.index, + '000000' + ); + + return { txId, payerAddress: buyer.address, signature }; +} + +async function retry(accept, { txId, payerAddress, signature }) { + // Multi-signature payload: one {address, signature} entry per unique input + // address of the on-chain tx. The CLI uses address-0 (getCurrentAddress on + // a fresh wallet returns index 0), so the array is length 1. + const payload = { + x402Version: 2, + scheme: accept.scheme, + network: accept.network, + payload: { + txId, + signatures: [{ address: payerAddress, signature }], + requestId: accept.extra.requestId, + }, + }; + const headerB64 = Buffer.from(JSON.stringify(payload)).toString('base64'); + + log('CLIENT', 'Retrying GET with PAYMENT-SIGNATURE ...'); + const resp = await fetch(url, { headers: { 'PAYMENT-SIGNATURE': headerB64 } }); + const body = await resp.json(); + if (!resp.ok) { + throw new Error(`server rejected payment: ${JSON.stringify(body)}`); + } + return body; +} + +// --- main ------------------------------------------------------------------- + +async function main() { + log('CLIENT', `=== x402 hathor-direct demo — route=${args.route} ===`); + log('CLIENT', `Target URL: ${url}`); + + const wallet = await startWallet(); + try { + const accept = await fetch402(); + log('CLIENT', `402 received — scheme=${accept.scheme} amount=${accept.amount}`); + + const proof = await pay(wallet, accept); + const result = await retry(accept, proof); + + log('CLIENT', ''); + log('CLIENT', '=== resource ==='); + log('CLIENT', JSON.stringify(result.data, null, 2)); + log('CLIENT', ''); + if (result.payment) { + log('CLIENT', '=== payment ==='); + log('CLIENT', JSON.stringify(result.payment, null, 2)); + } + } finally { + try { await wallet.stop(); } catch (_) { /* ignore */ } + } +} + +main().catch((err) => { + log('CLIENT', `ERROR: ${err.message}`); + // eslint-disable-next-line no-console + console.error(err); + process.exit(1); +}); diff --git a/config.js b/config.js new file mode 100644 index 0000000..4197691 --- /dev/null +++ b/config.js @@ -0,0 +1,57 @@ +// x402 PoC Configuration (hathor-direct scheme — no nano contracts). +// All knobs are overrideable via environment variables. + +'use strict'; + +const env = process.env; + +const int = (v, dflt) => { + const n = parseInt(v, 10); + return Number.isFinite(n) ? n : dflt; +}; + +module.exports = { + // Hathor fullnode (read-only) — used by the verifier and the dApp. + fullnodeUrl: env.FULLNODE_URL || 'https://node1.testnet.hathor.network', + network: env.HATHOR_NETWORK || 'testnet', + htrTokenUid: '00', + + // Resource server + resourceServerPort: int(env.RESOURCE_SERVER_PORT, 3000), + resourceServerPublicUrl: env.RESOURCE_SERVER_PUBLIC_URL || 'http://localhost:3000', + + // Standalone facilitator (optional in compose — only used when VERIFY_MODE=facilitator) + facilitatorPort: int(env.FACILITATOR_PORT, 8402), + facilitatorUrl: env.FACILITATOR_URL || `http://localhost:${int(env.FACILITATOR_PORT, 8402)}`, + + // The seller's receiving address. Static config — the resource server never + // touches a wallet for the exact scheme. For the upto scheme, a seller-side + // wallet-headless issues the refund tx (see refundIssuer.js). + sellerAddress: env.SELLER_ADDRESS || '', + + // Seller wallet-headless (only used by the upto scheme to issue refunds). + sellerWalletUrl: env.SELLER_WALLET_URL || 'http://wallet-headless:8000', + sellerWalletId: env.SELLER_WALLET_ID || 'seller', + + // Server secret used to HMAC requestIds. If unset we fall back to a fixed + // string so mint+verify use the same key (otherwise empty=='' on verify + // wouldn't match the fallback used at mint, causing silent bad_request_id_mac + // failures). Set SERVER_SECRET in prod — startup logs a clear warning otherwise. + serverSecret: env.SERVER_SECRET || 'x402-poc-dev-secret-do-not-use-in-prod', + requestIdTtlSeconds: int(env.REQUEST_ID_TTL_SECONDS, 120), + + // Dedup + blocklist store (better-sqlite3 file). + dedupDbPath: env.DEDUP_DB_PATH || './data/payments.sqlite', + + // Zero-conf safety threshold: amounts above this require meta.first_block. + // Default is "no cap" so all amounts use zero-conf; set lower in prod. + zeroConfMaxAmount: int(env.ZERO_CONF_MAX_AMOUNT, Number.MAX_SAFE_INTEGER), + + // Verify path: 'self' (resource-server imports verifier directly) or + // 'facilitator' (resource-server calls /x402/verify over HTTP). + verifyMode: env.VERIFY_MODE === 'facilitator' ? 'facilitator' : 'self', + + // Default per-route prices (atomic units; 1 HTR = 100). + htrPaymentAmount: int(env.HTR_PAYMENT_AMOUNT, 100), + generateMaxPrice: int(env.GENERATE_MAX_PRICE, 500), +}; diff --git a/dapp/.claude/commands/hathor-dapp.md b/dapp/.claude/commands/hathor-dapp.md new file mode 100644 index 0000000..87fecc3 --- /dev/null +++ b/dapp/.claude/commands/hathor-dapp.md @@ -0,0 +1,270 @@ +# Hathor dApp Development Assistant + +You are an expert assistant for building decentralized applications on the Hathor Network using the create-hathor-dapp template. + +## Your Capabilities + +You help developers with: +1. **Scaffolding** - Setting up new dApps, configuring environment, project structure +2. **Nano Contract Integration** - Adding contract methods, reading state, handling transactions +3. **Wallet Integration** - Reown (WalletConnect) and MetaMask Snaps setup +4. **Feature Development** - Building complete features following template patterns +5. **Testing** - Unit, integration, and E2E tests + +## Template Architecture + +### Context Providers (in order of wrapping) +``` +ToastProvider + └── WalletConnectProvider (Reown integration) + └── MetaMaskProvider (Snaps integration) + └── UnifiedWalletProvider (unified adapter) + └── WalletProvider (balance/transactions) + └── HathorProvider (network/contracts) +``` + +### Key Files +- `contexts/HathorContext.tsx` - Network, contract state, connection status +- `contexts/WalletContext.tsx` - Balance management, `sendNanoContractTx()` +- `contexts/WalletConnectContext.tsx` - Reown/WalletConnect client +- `contexts/MetaMaskContext.tsx` - MetaMask Snaps integration +- `lib/hathorRPC.ts` - RPC service for wallet communication +- `lib/hathorCoreAPI.ts` - Blockchain data queries (state, history, transactions) +- `lib/config.ts` - Environment configuration + +### Wallet Connection Flow +1. User clicks "Connect Wallet" → `WalletConnectionModal` opens +2. User selects Reown or MetaMask Snaps +3. Provider's `connect()` is called +4. `UnifiedWalletContext` provides unified `WalletAdapter` interface +5. `HathorContext` syncs address and network +6. `WalletContext` fetches balance via `HathorRPCService` + +## Common Patterns + +### Sending a Nano Contract Transaction +```typescript +import { useWallet } from '@/contexts/WalletContext'; +import { useHathor } from '@/contexts/HathorContext'; +import { toast } from '@/lib/toast'; + +function MyComponent() { + const { sendNanoContractTx, refreshBalance } = useWallet(); + const { network, getContractIdForToken, getContractStateForToken } = useHathor(); + + const handleAction = async () => { + const contractId = getContractIdForToken('HTR'); + const contractState = getContractStateForToken('HTR'); + const tokenUid = contractState?.token_uid || '00'; + + try { + toast.info('Please confirm in your wallet...'); + + const result = await sendNanoContractTx({ + network, + nc_id: contractId, + method: 'your_method_name', + args: [arg1, arg2], + actions: [ + { + type: 'deposit', // or 'withdrawal' + amount: '1000', // in cents (10.00 tokens) + token: tokenUid, + }, + ], + push_tx: true, + }); + + toast.success(`TX: ${result.response?.hash?.slice(0, 10)}...`); + refreshBalance(tokenUid, network); + } catch (error: any) { + toast.error(error.message || 'Transaction failed'); + } + }; +} +``` + +### Reading Contract State +```typescript +import { useHathor } from '@/contexts/HathorContext'; + +function ContractInfo() { + const { getContractStateForToken, coreAPI } = useHathor(); + + // Cached state from context + const state = getContractStateForToken('HTR'); + + // Direct API calls + const fetchData = async () => { + const state = await coreAPI.getContractState('contract_id', ['field1', 'field2']); + const history = await coreAPI.getContractHistory('contract_id', 100); + const balance = await coreAPI.getClaimableBalance('contract_id', userAddress); + }; +} +``` + +### Creating a New Page +```typescript +// app/my-feature/page.tsx +'use client'; + +import { useHathor } from '@/contexts/HathorContext'; +import { useWallet } from '@/contexts/WalletContext'; +import Header from '@/components/Header'; + +export default function MyFeaturePage() { + const { isConnected, network } = useHathor(); + const { balance } = useWallet(); + + return ( +
+
{}} appName="My dApp" /> +
+ {isConnected ? ( +
{/* Your connected UI */}
+ ) : ( +
{/* Connect wallet prompt */}
+ )} +
+
+ ); +} +``` + +### Adding Contract State Fields +1. Update `types/hathor.ts`: +```typescript +export interface ContractState { + token_uid: string; + available_tokens: bigint; + total_liquidity_provided: bigint; + your_new_field: number; // Add your field + [key: string]: any; +} +``` + +2. Update `lib/hathorCoreAPI.ts` to fetch the field: +```typescript +async getContractState(contractId: string, fields: string[] = [ + 'token_uid', 'available_tokens', 'total_liquidity_provided', 'your_new_field' +]): Promise +``` + +## Reown (WalletConnect) Integration + +### Configuration +Edit `lib/walletConnectConfig.ts`: +```typescript +export const walletConnectMetadata = { + name: 'Your dApp Name', + description: 'Your dApp description', + url: 'https://yourdapp.com', + icons: ['https://yourdapp.com/icon.png'], +}; +``` + +### Environment Variables +```env +NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_project_id # From https://cloud.walletconnect.com/ +``` + +### How It Works +- `WalletConnectContext` manages the SignClient +- Sessions are persisted and restored on page load +- Network is synced from wallet session +- RPC calls go through `HathorRPCService` with WalletConnect client + +## MetaMask Snaps Integration + +### How It Works +- `MetaMaskContext` manages Snap connection +- Uses `wallet_requestSnaps` to install/connect +- RPC calls use `wallet_invokeSnap` with Hathor Snap ID +- Supports same methods as WalletConnect + +### Snap ID +The Hathor Snap ID is configured in `MetaMaskContext.tsx`. + +## Testing + +### Unit Tests +```typescript +// __tests__/unit/myComponent.test.ts +import { describe, it, expect, vi } from 'vitest'; +import { render, screen } from '@testing-library/react'; + +describe('MyComponent', () => { + it('should render correctly', () => { + // Test implementation + }); +}); +``` + +### Mocking Contexts +```typescript +vi.mock('@/contexts/HathorContext', () => ({ + useHathor: () => ({ + isConnected: true, + network: 'testnet', + getContractStateForToken: vi.fn(), + }), +})); +``` + +### E2E Tests +```typescript +// e2e/my-feature.spec.ts +import { test, expect } from '@playwright/test'; + +test('my feature works', async ({ page }) => { + await page.goto('/'); + // Test implementation +}); +``` + +## Environment Setup + +### Required Variables +```env +NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=xxx # Required for Reown +NEXT_PUBLIC_DEFAULT_NETWORK=testnet # testnet or mainnet +NEXT_PUBLIC_CONTRACT_IDS_TESTNET=["..."] # Your contract IDs +NEXT_PUBLIC_CONTRACT_IDS_MAINNET=[] +``` + +### Optional Variables +```env +NEXT_PUBLIC_USE_MOCK_WALLET=true # Enable mock mode for development +NEXT_PUBLIC_APP_NAME=My dApp +NEXT_PUBLIC_APP_DESCRIPTION=Description +``` + +## Development Workflow + +1. **Start with mock mode** - `NEXT_PUBLIC_USE_MOCK_WALLET=true` +2. **Build your UI** - Create components and pages +3. **Add contract integration** - Use `sendNanoContractTx` and `coreAPI` +4. **Test with real wallet** - Set `NEXT_PUBLIC_USE_MOCK_WALLET=false` +5. **Write tests** - Unit tests for logic, E2E for flows + +## Commands +```bash +npm run dev # Development server +npm run build # Production build +npm run test # Unit tests +npm run test:coverage # Tests with coverage +npm run test:e2e # E2E tests +npm run lint # ESLint +``` + +## When Helping Users + +1. **Always read relevant files first** before suggesting changes +2. **Follow existing patterns** in the codebase +3. **Use the template's utilities** (toast, formatBalance, etc.) +4. **Maintain type safety** - update types when adding features +5. **Consider both wallet types** - Reown and MetaMask Snaps +6. **Handle loading and error states** appropriately +7. **Write tests** for new functionality + +$ARGUMENTS diff --git a/dapp/.env.example b/dapp/.env.example new file mode 100644 index 0000000..a0fa718 --- /dev/null +++ b/dapp/.env.example @@ -0,0 +1,35 @@ +# =========================================== +# x402 Hathor dApp Configuration +# =========================================== + +# WalletConnect / Reown Project ID (Required) +# Get yours from https://cloud.walletconnect.com/ +NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_project_id_here + +# =========================================== +# Network +# =========================================== + +# Default network: 'testnet' or 'mainnet' +NEXT_PUBLIC_DEFAULT_NETWORK=testnet + +# Enable mock wallet for UI development (no real wallet needed) +NEXT_PUBLIC_USE_MOCK_WALLET=false + +# Hathor fullnode URLs (per network) +NEXT_PUBLIC_HATHOR_NODE_URL_TESTNET=https://node1.testnet.hathor.network/v1a +NEXT_PUBLIC_HATHOR_NODE_URL_MAINNET=https://node1.mainnet.hathor.network/v1a + +# =========================================== +# x402 resource server +# =========================================== + +# Where the dApp will fetch paid resources from. The URL input box is seeded +# with `${NEXT_PUBLIC_RESOURCE_SERVER_URL}/weather` by default. +NEXT_PUBLIC_RESOURCE_SERVER_URL=http://localhost:3000 + +# =========================================== +# App metadata (shown in WalletConnect modals) +# =========================================== +NEXT_PUBLIC_APP_NAME=x402 Client +NEXT_PUBLIC_APP_DESCRIPTION=x402 hathor-direct payment client (no nano contracts) diff --git a/dapp/.env.local.example b/dapp/.env.local.example new file mode 100644 index 0000000..b2a62a4 --- /dev/null +++ b/dapp/.env.local.example @@ -0,0 +1,12 @@ +# Local dev quick-start — uses the mock wallet so you don't need a real wallet. +NEXT_PUBLIC_USE_MOCK_WALLET=true + +# Network +NEXT_PUBLIC_DEFAULT_NETWORK=testnet + +# Hathor node URLs +NEXT_PUBLIC_HATHOR_NODE_URL_TESTNET=https://node1.testnet.hathor.network/v1a +NEXT_PUBLIC_HATHOR_NODE_URL_MAINNET=https://node1.mainnet.hathor.network/v1a + +# Where /weather, /generate live (the local resource-server) +NEXT_PUBLIC_RESOURCE_SERVER_URL=http://localhost:3000 diff --git a/dapp/.eslintrc.json b/dapp/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/dapp/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/dapp/.github/actions/deploy-dapp/action.yml b/dapp/.github/actions/deploy-dapp/action.yml new file mode 100644 index 0000000..ee0cdee --- /dev/null +++ b/dapp/.github/actions/deploy-dapp/action.yml @@ -0,0 +1,48 @@ +name: Deploy Hathor dApp +description: Build and deploy the Hathor dApp to AWS S3/CloudFront + +inputs: + site: + description: The site name to build and deploy (staging, production) + required: true + aws-region: + description: The AWS region to deploy to + required: true + default: us-east-1 + aws-role: + description: The AWS IAM role to assume for deployment + required: true + +runs: + using: composite + steps: + - name: Configure AWS Credentials + # https://github.com/aws-actions/configure-aws-credentials/releases/tag/v5.1.1 + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 + with: + aws-region: ${{ inputs.aws-region }} + role-to-assume: ${{ inputs.aws-role }} + + - name: Set up Node.js 24.x + # https://github.com/actions/setup-node/releases/tag/v6.1.0 + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f + with: + node-version: 24.x + + - name: Download node modules + # https://github.com/actions/download-artifact/releases/tag/v6.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 + with: + name: node_modules + + - name: Build + shell: bash + run: | + tar -xf node_modules.tar + make build site=${{ inputs.site }} + + - name: Deploy + shell: bash + run: | + make sync site=${{ inputs.site }} + make clear_cloudfront_cache site=${{ inputs.site }} diff --git a/dapp/.github/workflows/deploy.yml b/dapp/.github/workflows/deploy.yml new file mode 100644 index 0000000..9105fca --- /dev/null +++ b/dapp/.github/workflows/deploy.yml @@ -0,0 +1,78 @@ +name: deploy + +on: + push: + branches: [release] + tags: ['v*'] + +permissions: + id-token: write + contents: read + +jobs: + dependencies: + runs-on: ubuntu-latest + steps: + # https://github.com/actions/checkout/releases/tag/v6.0.1 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 + + - name: Set up Node.js 24.x + # https://github.com/actions/setup-node/releases/tag/v6.1.0 + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f + with: + node-version: 24.x + + - name: Cache node modules + # https://github.com/actions/cache/releases/tag/v4.3.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 + env: + cache-name: cache-node-modules + with: + path: ${{ github.workspace }}/node_modules + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Install Dependencies + run: | + npm install + tar -cvf node_modules.tar ./node_modules + + - name: Upload node modules + # https://github.com/actions/upload-artifact/releases/tag/v5.0.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 + with: + name: node_modules + path: node_modules.tar + if-no-files-found: error + retention-days: 1 + + deploy-staging: + if: github.ref == 'refs/heads/release' + needs: dependencies + runs-on: ubuntu-latest + steps: + # https://github.com/actions/checkout/releases/tag/v6.0.1 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 + - uses: ./.github/actions/deploy-dapp + with: + site: staging + aws-region: us-east-1 + # TODO: Update with your AWS IAM role ARN + aws-role: arn:aws:iam::YOUR_ACCOUNT_ID:role/YourGitHubActionsRoleStaging + + deploy-production: + if: startsWith(github.ref, 'refs/tags/v') + needs: dependencies + runs-on: ubuntu-latest + steps: + # https://github.com/actions/checkout/releases/tag/v6.0.1 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 + - uses: ./.github/actions/deploy-dapp + with: + site: production + aws-region: us-east-1 + # TODO: Update with your AWS IAM role ARN + aws-role: arn:aws:iam::YOUR_ACCOUNT_ID:role/YourGitHubActionsRoleProduction diff --git a/dapp/.github/workflows/test.yml b/dapp/.github/workflows/test.yml new file mode 100644 index 0000000..d5f7c7d --- /dev/null +++ b/dapp/.github/workflows/test.yml @@ -0,0 +1,118 @@ +name: Tests + +on: + push: + branches: [main, master, develop] + pull_request: + branches: [main, master, develop] + +jobs: + unit-and-integration-tests: + name: Unit & Integration Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run unit and integration tests + run: npm run test:run + + - name: Generate coverage report + run: npm run test:coverage + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + files: ./coverage/lcov.info + flags: unittests + name: codecov-umbrella + fail_ci_if_error: false + + e2e-tests: + name: E2E Tests + runs-on: ubuntu-latest + needs: unit-and-integration-tests + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Install Playwright browsers + run: npx playwright install --with-deps + + - name: Run E2E tests + run: npm run test:e2e + env: + NEXT_PUBLIC_USE_MOCK_WALLET: 'true' + NEXT_PUBLIC_DEFAULT_NETWORK: 'testnet' + + - name: Upload Playwright report + uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 + + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run linter + run: npm run lint + + build: + name: Build + runs-on: ubuntu-latest + needs: [unit-and-integration-tests, lint] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build application + run: npm run build + env: + NEXT_PUBLIC_USE_MOCK_WALLET: 'false' + NEXT_PUBLIC_DEFAULT_NETWORK: 'testnet' diff --git a/dapp/.gitignore b/dapp/.gitignore new file mode 100644 index 0000000..2ca33b2 --- /dev/null +++ b/dapp/.gitignore @@ -0,0 +1,14 @@ +node_modules +.next +out +dist +build +.DS_Store +*.log +.env*.local +.vercel +*.tsbuildinfo +next-env.d.ts +coverage/ +playwright-report/ +test-results/ diff --git a/dapp/AGENTS.md b/dapp/AGENTS.md new file mode 100644 index 0000000..f660af8 --- /dev/null +++ b/dapp/AGENTS.md @@ -0,0 +1,124 @@ +# AGENTS.md — LLM instructions for the x402 Hathor dApp + +Source of truth for AI agents (LLMs/IDEs) extending or fixing this dApp. + +## Snapshot + +| Concept | Where | +|---|---| +| WalletConnect Project ID | `.env.local` → `NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID` | +| Default network | `.env.local` → `NEXT_PUBLIC_DEFAULT_NETWORK` | +| Mock wallet | `.env.local` → `NEXT_PUBLIC_USE_MOCK_WALLET=true` | +| Resource server URL | `.env.local` → `NEXT_PUBLIC_RESOURCE_SERVER_URL` | +| Main page | `app/page.tsx` | +| Payment detail | `app/payment/[txId]/page.tsx` | +| The whole flow | `components/X402Fetch.tsx` | +| RPC wrappers | `lib/hathorRPC.ts` (`sendTransaction`, `signWithAddress`, `getAddress`, `getBalance`) | +| Wallet ctx | `contexts/WalletContext.tsx` | +| Hathor ctx | `contexts/HathorContext.tsx` | +| Local history | `lib/paymentHistory.ts` | + +## Available wallet RPC methods + +All implemented today across the wallet stack (desktop / mobile / MetaMask +snap). Use them via the typed wrappers in `lib/hathorRPC.ts` and exposed +through `useWallet()` in `contexts/WalletContext.tsx`. + +| RPC | Wrapper | When to call | +|---|---|---| +| `htr_getAddress` | `getAddress({network, type})` | Before sending/signing, to get an empty address + index | +| `htr_sendTransaction` | `sendTransaction({network, outputs, push_tx, changeAddress?})` | Pay the seller for a 402 | +| `htr_signWithAddress` | `signWithAddress({network, message, addressIndex})` | Sign the server's `requestId` | +| `htr_getBalance` | `getBalance({network, tokens})` | Show the balance card | +| `htr_getConnectedNetwork` | `getConnectedNetwork()` | Connection diagnostics | + +## The x402 flow (single source of truth) + +```ts +// 1. Fetch the URL. +const r = await fetch(url, { mode: 'cors' }); +if (r.status !== 402) return r; + +// 2. Pick the first accept option. +const accept = (await r.json()).accepts[0]; + +// 3. Grab an empty address + index. We use it as changeAddress so it appears +// in the tx (the verifier accepts the signing address in either inputs or +// outputs), and as addressIndex when signing. +const buyer = await getAddress({ network, type: 'first_empty' }); + +// 4. Send the payment. +const sendResp = await sendTransaction({ + network, + outputs: [{ address: accept.payTo, value: parseInt(accept.amount, 10), + token: accept.asset === '00' ? undefined : accept.asset }], + changeAddress: buyer.address, + push_tx: true, +}); +const txId = HathorRPCService.extractTxId(sendResp); + +// 5. Wait for the fullnode to see the tx (~1–2s, no first_block required). +await pollMempool(nodeUrl, txId); + +// 6. Sign the requestId. +const signed = await signWithAddress({ + network, message: accept.extra.requestId, addressIndex: buyer.index, +}); + +// 7. Retry with the proof. +const headerB64 = btoa(JSON.stringify({ + x402Version: 2, scheme: accept.scheme, network: accept.network, + payload: { + txId, payerAddress: signed.address.address, + signature: signed.signature, requestId: accept.extra.requestId, + }, +})); +const r2 = await fetch(url, { mode: 'cors', headers: { 'PAYMENT-SIGNATURE': headerB64 }}); +``` + +## Mock mode + +Set `NEXT_PUBLIC_USE_MOCK_WALLET=true` and `HathorRPCService` will return +deterministic fakes for `htr_getBalance`, `htr_getAddress`, +`htr_sendTransaction`, and `htr_signWithAddress`. Useful for UI work without +a real wallet. The `addressIndex` flows through correctly so the signing / +sending UI states all render. + +## Adding new pages + +Standard Next.js App Router. Add `app//page.tsx`. All providers are +already mounted in `app/layout.tsx`: + +``` +ToastProvider → WalletConnectProvider → MetaMaskProvider → +UnifiedWalletProvider → WalletProvider → HathorProvider +``` + +Don't reorder; later providers depend on earlier ones. + +## What this dApp is NOT + +- Not a nano-contract dApp. There is no `htr_sendNanoContractTx`, no contract + state polling, no blueprints. The old escrow / channel / contract UI was + removed when the project switched to `hathor-direct`. +- Not a wallet-management dApp. There is no key custody, no on-chain + history beyond a localStorage list of payments this browser has made. + +## Tests + +- `__tests__/unit/` — vitest unit tests +- `__tests__/integration/` — vitest integration (mocked wallets) +- `e2e/` — Playwright + +`npm run lint` · `npm run test:run` · `npm run test:e2e` + +## Files you should NOT modify casually + +- `contexts/WalletConnectContext.tsx`, `contexts/MetaMaskContext.tsx`, + `contexts/UnifiedWalletContext.tsx` — adapter unification. Tricky. +- `lib/walletConnectClient.ts` — Reown client singleton. +- `app/layout.tsx` — provider ordering. + +## License + +MIT diff --git a/dapp/Dockerfile b/dapp/Dockerfile new file mode 100644 index 0000000..c25d581 --- /dev/null +++ b/dapp/Dockerfile @@ -0,0 +1,57 @@ +# dApp Dockerfile — multi-stage Next.js standalone build. +FROM node:22-alpine AS builder + +WORKDIR /app + +COPY package.json package-lock.json* ./ +RUN npm ci + +COPY . . + +# x402 build-time env vars. NEXT_PUBLIC_* are baked into the bundle. +ARG NEXT_PUBLIC_USE_MOCK_WALLET=false +ARG NEXT_PUBLIC_DEFAULT_NETWORK=testnet +ARG NEXT_PUBLIC_HATHOR_NODE_URL_TESTNET=https://node1.testnet.hathor.network/v1a +ARG NEXT_PUBLIC_HATHOR_NODE_URL_MAINNET=https://node1.mainnet.hathor.network/v1a +ARG NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=8264fff563181da658ce64ee80e80458 +ARG NEXT_PUBLIC_RESOURCE_SERVER_URL=http://localhost:3000 + +ENV NEXT_PUBLIC_USE_MOCK_WALLET=${NEXT_PUBLIC_USE_MOCK_WALLET} +ENV NEXT_PUBLIC_DEFAULT_NETWORK=${NEXT_PUBLIC_DEFAULT_NETWORK} +ENV NEXT_PUBLIC_HATHOR_NODE_URL_TESTNET=${NEXT_PUBLIC_HATHOR_NODE_URL_TESTNET} +ENV NEXT_PUBLIC_HATHOR_NODE_URL_MAINNET=${NEXT_PUBLIC_HATHOR_NODE_URL_MAINNET} +ENV NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=${NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID} +ENV NEXT_PUBLIC_RESOURCE_SERVER_URL=${NEXT_PUBLIC_RESOURCE_SERVER_URL} + +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN npm run build + +# --- Runtime stage --------------------------------------------------------- +FROM node:22-alpine AS runner + +RUN apk add --no-cache curl + +WORKDIR /app + +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static +COPY --from=builder /app/public ./public + +RUN addgroup -g 1001 -S nodejs && \ + adduser -S nextjs -G nodejs && \ + chown -R nextjs:nodejs /app + +USER nextjs + +EXPOSE 3000 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:3000/ || exit 1 + +ENV HOSTNAME=0.0.0.0 + +CMD ["node", "server.js"] diff --git a/dapp/Makefile b/dapp/Makefile new file mode 100644 index 0000000..6fa851f --- /dev/null +++ b/dapp/Makefile @@ -0,0 +1,14 @@ +.PHONY: build +build: + ./scripts/deploy.sh $(site) build $(aws_profile) + +.PHONY: sync +sync: + ./scripts/deploy.sh $(site) sync $(aws_profile) + +.PHONY: deploy +deploy: build sync clear_cloudfront_cache + +.PHONY: clear_cloudfront_cache +clear_cloudfront_cache: + ./scripts/deploy.sh $(site) clear_cache $(aws_profile) diff --git a/dapp/QUICKSTART.md b/dapp/QUICKSTART.md new file mode 100644 index 0000000..6eba9cf --- /dev/null +++ b/dapp/QUICKSTART.md @@ -0,0 +1,103 @@ +# Quick Start Guide + +## Getting Started + +### 1. Install Dependencies +```bash +npm install +``` + +### 2. Configure Environment +```bash +cp .env.local.example .env.local +``` + +The default configuration uses mock wallet mode, so you can test immediately without a real wallet. + +### 3. Run Development Server +```bash +npm run dev +``` + +### 4. Open in Browser +Navigate to [http://localhost:3000](http://localhost:3000) + +## Testing with Mock Wallet + +With `NEXT_PUBLIC_USE_MOCK_WALLET=true` (default), you can: + +1. **Connect Wallet**: Click "Connect Wallet" and choose any option +2. **View Mock Balance**: See simulated HTR balance +3. **View Contract Info**: See mock contract state + +All actions are simulated and no real transactions occur. + +## Connecting a Real Wallet + +### Prerequisites +- Hathor Wallet extension or MetaMask with Hathor Snap +- HTR tokens on India Testnet +- Deployed nano contract + +### Steps + +1. **Update Environment**: +```env +NEXT_PUBLIC_USE_MOCK_WALLET=false +NEXT_PUBLIC_CONTRACT_IDS_TESTNET=["your_contract_id_here"] +``` + +2. **Restart Server**: +```bash +npm run dev +``` + +3. **Connect Wallet**: + - Click "Connect Wallet" + - Choose Reown or MetaMask Snaps + - Approve connection in wallet + +4. **Interact with Contract**: + - Use the provided hooks and utilities + - Send transactions via `sendNanoContractTx` + - Confirm transactions in your wallet + +## Network Selection + +Use the network selector in the top-right to switch between: +- **India Testnet** (active) +- **Mainnet** (coming soon) + +## Troubleshooting + +### "Wallet not connected" error +- Ensure wallet extension is installed and unlocked +- Try refreshing the page +- Check that you're on the correct network + +### "Contract state not loaded" +- Verify `NEXT_PUBLIC_CONTRACT_IDS_TESTNET` is set correctly +- Check that the contract exists on the selected network +- Ensure node URL is accessible + +### Build errors +- Run `npm install` to ensure all dependencies are installed +- Delete `.next` folder and rebuild: `rm -rf .next && npm run dev` +- Check that Node.js version is 18 or higher + +## Next Steps + +1. **Deploy Contract**: Deploy your nano contract to India Testnet +2. **Update Config**: Add contract ID to `.env.local` +3. **Build Your dApp**: Modify the template to add your features + +## Development Tips + +- Use mock mode for rapid UI development +- Monitor browser console for errors +- Use React DevTools to inspect component state + +## Support + +- Check [README.md](./README.md) for detailed documentation +- Open an issue on GitHub for bugs or questions diff --git a/dapp/README.md b/dapp/README.md new file mode 100644 index 0000000..22a012b --- /dev/null +++ b/dapp/README.md @@ -0,0 +1,80 @@ +# x402 Hathor dApp + +A browser-based x402 payment client for Hathor Network's **`hathor-direct`** +scheme — regular UTXO payments, no nano contracts. + +Built on Next.js 14 + React 18 + Tailwind. Wallet integration is +WalletConnect/Reown (desktop wallet, mobile wallet) and MetaMask Snaps via a +unified adapter. + +## What it does + +1. Connect a Hathor wallet (WalletConnect / Reown / MetaMask Snap). +2. Enter an x402-enabled URL. +3. On 402, the dApp shows the payment requirement. +4. Click **Pay & Access** — the wallet signs a regular send-tx + a message + signature over the server's `requestId`. +5. The dApp retries the request with the proof header; the resource appears + and a row is recorded in the local payment history. +6. Click any row to open `/payment/[txId]` for an on-chain detail view. + +## Setup + +```bash +npm install +cp .env.example .env.local +$EDITOR .env.local # set NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID +npm run dev +``` + +Then open and connect a wallet that supports +`htr_sendTransaction` and `htr_signWithAddress` (desktop, mobile, MetaMask snap +— all of these do as of the rpc-handler version pinned in this repo). + +## Environment + +```env +NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=... # https://cloud.walletconnect.com/ +NEXT_PUBLIC_DEFAULT_NETWORK=testnet +NEXT_PUBLIC_USE_MOCK_WALLET=false # true = simulate the wallet (no real RPC) +NEXT_PUBLIC_HATHOR_NODE_URL_TESTNET=https://node1.testnet.hathor.network/v1a +NEXT_PUBLIC_HATHOR_NODE_URL_MAINNET=https://node1.mainnet.hathor.network/v1a +NEXT_PUBLIC_RESOURCE_SERVER_URL=http://localhost:3000 # where /weather, /generate live +``` + +## Architecture (short) + +- `lib/hathorRPC.ts` — typed wrappers around `htr_sendTransaction` and + `htr_signWithAddress` (plus `htr_getAddress`, `htr_getBalance`, + `htr_getConnectedNetwork`). The mock branch lets you exercise the UI without + a real wallet. +- `contexts/WalletContext.tsx` — exposes `sendTransaction`, `signWithAddress`, + `getAddress`, plus balance helpers. +- `contexts/HathorContext.tsx` — connection state, network, fullnode read API. +- `components/X402Fetch.tsx` — the entire user flow (fetch → 402 → pay → sign + → retry → show data). One state machine. +- `lib/paymentHistory.ts` — localStorage-backed list of past payments. +- `app/payment/[txId]/page.tsx` — fetches the tx from the fullnode and + highlights the paying output / shows void/confirm status. + +## Testing + +```bash +npm run lint +npm run test:run # vitest unit + integration +npm run test:e2e # Playwright (mock-wallet mode) +``` + +## Production build + +```bash +npm run build +npm start +``` + +For the static-export S3+CloudFront deploy used by `https://x402.hathor.dev`, +see `scripts/deploy.sh` and `.github/workflows/deploy.yml`. + +## License + +MIT diff --git a/dapp/__mocks__/hathorCoreAPI.ts b/dapp/__mocks__/hathorCoreAPI.ts new file mode 100644 index 0000000..a5c9ac9 --- /dev/null +++ b/dapp/__mocks__/hathorCoreAPI.ts @@ -0,0 +1,181 @@ +import { vi } from 'vitest' +import { ContractState, BlueprintInfo, ContractHistory } from '@/types/hathor' +import { Network } from '@/lib/config' + +export const mockBlueprintInfo: BlueprintInfo = { + id: 'test-blueprint-id', + name: 'TestContract', + attributes: { + public_methods: ['deposit', 'withdraw', 'get_balance'], + }, +} + +export const mockContractStates: Record = { + 'contract-htr': { + token_uid: '00', + available_tokens: 100000000n, + total_liquidity_provided: 100000000n, + }, + 'contract-usdc': { + token_uid: '01', + available_tokens: 50000000n, + total_liquidity_provided: 50000000n, + }, +} + +export const mockTransactions = [ + { + tx_id: '0000000000000001', + timestamp: Date.now() - 60000, + nc_method: 'deposit', + nc_caller: 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp', + first_block: '0000000000000abc', + is_voided: false, + nc_args_decoded: { + amount: 1000, + }, + nc_events: [ + { + type: 'Deposit', + data: '{"user":"WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp","amount":1000}', + }, + ], + }, + { + tx_id: '0000000000000002', + timestamp: Date.now() - 120000, + nc_method: 'withdraw', + nc_caller: 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp', + first_block: '0000000000000abd', + is_voided: false, + nc_args_decoded: { + amount: 500, + }, + nc_events: [ + { + type: 'Withdrawal', + data: '{"user":"WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp","amount":500}', + }, + ], + }, +] + +export const mockContractHistory: ContractHistory = { + transactions: mockTransactions, + total: mockTransactions.length, + hasMore: false, +} + +export class MockHathorCoreAPI { + private baseUrl: string + public network: Network + + constructor(network: Network) { + this.network = network + this.baseUrl = `https://mock-${network}.hathor.network/v1a` + } + + async getBlueprintInfo(blueprintId: string): Promise { + return mockBlueprintInfo + } + + async getContractState(contractId: string): Promise { + return mockContractStates[contractId] || mockContractStates['contract-htr'] + } + + async getContractHistory( + contractId: string, + limit: number = 50, + after?: string + ): Promise { + // Simulate pagination + const allTransactions = mockTransactions + const startIndex = after ? allTransactions.findIndex(tx => tx.tx_id === after) + 1 : 0 + const transactions = allTransactions.slice(startIndex, startIndex + limit) + + return { + transactions, + total: transactions.length, + hasMore: startIndex + limit < allTransactions.length, + } + } + + async getTransaction(txId: string): Promise { + const tx = mockTransactions.find(t => t.tx_id === txId) + if (!tx) { + throw new Error(`Transaction not found: ${txId}`) + } + return tx + } + + async callViewFunction( + contractId: string, + method: string, + args: any[] = [], + callerAddress?: string + ): Promise { + const callKey = `${method}(${args.map(arg => JSON.stringify(arg)).join(', ')})` + + // Mock different view function responses + if (method === 'get_address_balance') { + return { + calls: { + [callKey]: { + value: 5000, + }, + }, + } + } + + if (method === 'get_balance') { + return { + calls: { + [callKey]: { + value: 10000, + }, + }, + } + } + + return { + calls: { + [callKey]: { + value: 0, + }, + }, + } + } + + async getClaimableBalance(contractId: string, callerAddress: string): Promise { + const result = await this.callViewFunction(contractId, 'get_address_balance', [callerAddress], callerAddress) + const callKey = Object.keys(result.calls)[0] + if (callKey && result.calls[callKey]?.value !== undefined) { + return BigInt(result.calls[callKey].value) + } + return 0n + } +} + +// Create mock instance +export const createMockHathorCoreAPI = (network: Network = 'testnet') => { + return new MockHathorCoreAPI(network) +} + +// Vitest mock factory +export const mockHathorCoreAPIFactory = () => { + const mockInstance = createMockHathorCoreAPI() + + return { + HathorCoreAPI: vi.fn().mockImplementation((network: Network) => { + const instance = createMockHathorCoreAPI(network) + return { + getBlueprintInfo: vi.fn().mockImplementation(instance.getBlueprintInfo.bind(instance)), + getContractState: vi.fn().mockImplementation(instance.getContractState.bind(instance)), + getContractHistory: vi.fn().mockImplementation(instance.getContractHistory.bind(instance)), + getTransaction: vi.fn().mockImplementation(instance.getTransaction.bind(instance)), + callViewFunction: vi.fn().mockImplementation(instance.callViewFunction.bind(instance)), + getClaimableBalance: vi.fn().mockImplementation(instance.getClaimableBalance.bind(instance)), + } + }), + } +} diff --git a/dapp/__mocks__/metamask.ts b/dapp/__mocks__/metamask.ts new file mode 100644 index 0000000..d78983a --- /dev/null +++ b/dapp/__mocks__/metamask.ts @@ -0,0 +1,179 @@ +import { vi } from 'vitest' + +export const mockAddress = 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp' +export const SNAP_ID = 'npm:@hathor/snap' + +// Mock balance response +export const mockBalanceResponse = { + response: [ + { + token: { id: '00', name: 'Hathor', symbol: 'HTR' }, + balance: { unlocked: 1250.5, locked: 0 }, + transactions: 42, + }, + { + token: { id: '01', name: 'USD Coin', symbol: 'USDC' }, + balance: { unlocked: 500.0, locked: 0 }, + transactions: 15, + }, + ], +} + +// Mock network response +export const mockNetworkResponse = { + network: 'testnet', + genesisHash: '000000000000000000000000000000000000000000000000000000000000test', +} + +// Mock wallet information response +export const mockWalletInfoResponse = { + address0: mockAddress, + network: 'testnet', +} + +// Mock transaction response +export const mockTxResponse = { + hash: '0000000000000000' + Math.random().toString(36).substring(2, 15), + success: true, +} + +// Create mock Ethereum provider (window.ethereum) +export const createMockEthereum = () => { + const eventHandlers: Record void>> = {} + + return { + isMetaMask: true, + request: vi.fn(async ({ method, params }: { method: string; params?: any }) => { + switch (method) { + case 'wallet_requestSnaps': + return { + [SNAP_ID]: { + id: SNAP_ID, + version: '1.0.0', + enabled: true, + }, + } + + case 'wallet_getSnaps': + return { + [SNAP_ID]: { + id: SNAP_ID, + version: '1.0.0', + enabled: true, + }, + } + + case 'wallet_invokeSnap': + const snapMethod = params?.request?.method + const snapParams = params?.request?.params + + switch (snapMethod) { + case 'htr_getAddress': + return { address: mockAddress, index: snapParams?.index || 0 } + + case 'htr_getBalance': + return mockBalanceResponse + + case 'htr_getConnectedNetwork': + return mockNetworkResponse + + case 'htr_getWalletInformation': + return mockWalletInfoResponse + + case 'htr_sendNanoContractTx': + return mockTxResponse + + case 'htr_changeNetwork': + return { + network: snapParams?.newNetwork || 'testnet', + success: true, + } + + default: + return { success: false, error: `Unknown snap method: ${snapMethod}` } + } + + default: + return { success: false, error: `Unknown Ethereum method: ${method}` } + } + }), + on: vi.fn((event: string, handler: (...args: any[]) => void) => { + if (!eventHandlers[event]) { + eventHandlers[event] = [] + } + eventHandlers[event].push(handler) + }), + removeListener: vi.fn((event: string, handler: (...args: any[]) => void) => { + if (eventHandlers[event]) { + eventHandlers[event] = eventHandlers[event].filter(h => h !== handler) + } + }), + emit: vi.fn((event: string, ...args: any[]) => { + if (eventHandlers[event]) { + eventHandlers[event].forEach(handler => handler(...args)) + } + }), + } +} + +// Setup MetaMask global mock +export const setupMetaMaskMock = () => { + const mockEthereum = createMockEthereum() + + Object.defineProperty(window, 'ethereum', { + writable: true, + configurable: true, + value: mockEthereum, + }) + + return mockEthereum +} + +// Remove MetaMask mock +export const cleanupMetaMaskMock = () => { + delete (window as any).ethereum +} + +// Mock MetaMask context value for testing +export const createMockMetaMaskContext = (overrides?: Partial) => ({ + address: mockAddress, + walletNetwork: 'testnet', + isConnected: true, + isInstalled: true, + connect: vi.fn(), + disconnect: vi.fn(), + request: vi.fn(async (method: string, params?: any) => { + const mockEthereum = createMockEthereum() + return mockEthereum.request({ + method: 'wallet_invokeSnap', + params: { + snapId: SNAP_ID, + request: { method, params }, + }, + }) + }), + ...overrides, +}) + +// Helper to test snap not installed scenario +export const createMockEthereumWithoutSnap = () => { + const mockEthereum = createMockEthereum() + + mockEthereum.request = vi.fn(async ({ method }: { method: string; params?: any }) => { + if (method === 'wallet_requestSnaps') { + throw new Error('User rejected snap installation') + } + throw new Error('Snap not installed') + }) + + return mockEthereum +} + +// Helper to test MetaMask not installed scenario +export const createMockWindowWithoutMetaMask = () => { + Object.defineProperty(window, 'ethereum', { + writable: true, + configurable: true, + value: undefined, + }) +} diff --git a/dapp/__mocks__/testUtils.tsx b/dapp/__mocks__/testUtils.tsx new file mode 100644 index 0000000..b4d2667 --- /dev/null +++ b/dapp/__mocks__/testUtils.tsx @@ -0,0 +1,118 @@ +import React, { ReactElement } from 'react' +import { render, RenderOptions } from '@testing-library/react' +import { vi } from 'vitest' + +// Mock fetch globally +export const mockFetch = vi.fn() +global.fetch = mockFetch + +// Helper to create mock fetch responses +export const createMockResponse = (data: any, status: number = 200) => { + return Promise.resolve({ + ok: status >= 200 && status < 300, + status, + statusText: status === 200 ? 'OK' : 'Error', + json: async () => data, + text: async () => JSON.stringify(data), + } as Response) +} + +// Helper to mock fetch with specific responses +export const setupMockFetch = (responses: Record) => { + mockFetch.mockImplementation(async (url: string) => { + const urlStr = url.toString() + + // Check for blueprint endpoint + if (urlStr.includes('/nc_blueprint/')) { + return createMockResponse(responses.blueprint || {}) + } + + // Check for contract state endpoint + if (urlStr.includes('/nano_contract/state')) { + if (urlStr.includes('calls[]=')) { + return createMockResponse(responses.viewFunction || { calls: {} }) + } + return createMockResponse(responses.contractState || { fields: {} }) + } + + // Check for contract history endpoint + if (urlStr.includes('/nano_contract/history')) { + return createMockResponse(responses.contractHistory || { history: [], has_more: false }) + } + + // Check for transaction endpoint + if (urlStr.includes('/transaction')) { + return createMockResponse(responses.transaction || {}) + } + + // Default response + return createMockResponse({}) + }) +} + +// Helper to reset fetch mock +export const resetMockFetch = () => { + mockFetch.mockReset() +} + +// Test wrapper with all providers +interface AllTheProvidersProps { + children: React.ReactNode +} + +export const AllTheProviders: React.FC = ({ children }) => { + return <>{children} +} + +// Custom render function that includes providers +export const renderWithProviders = ( + ui: ReactElement, + options?: Omit +) => { + return render(ui, { wrapper: AllTheProviders, ...options }) +} + +// Helper to wait for async operations +export const waitForAsync = () => new Promise(resolve => setTimeout(resolve, 0)) + +// Helper to create mock timers +export const setupMockTimers = () => { + vi.useFakeTimers() + return { + advanceTime: (ms: number) => vi.advanceTimersByTime(ms), + runAll: () => vi.runAllTimers(), + cleanup: () => vi.useRealTimers(), + } +} + +// Helper to mock console methods +export const mockConsole = () => { + const originalConsole = { ...console } + + const mocks = { + log: vi.spyOn(console, 'log').mockImplementation(() => {}), + error: vi.spyOn(console, 'error').mockImplementation(() => {}), + warn: vi.spyOn(console, 'warn').mockImplementation(() => {}), + } + + return { + ...mocks, + restore: () => { + Object.assign(console, originalConsole) + }, + } +} + +// Helper to create test addresses +export const createTestAddress = (suffix: string = '') => { + return `WYBwT3xLpDnHNtYZiU52oanupVeDKhAvN${suffix || 'p'}` +} + +// Helper to create test transaction hash +export const createTestTxHash = () => { + return '00000000' + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) +} + +// Re-export everything from React Testing Library +export * from '@testing-library/react' +export { default as userEvent } from '@testing-library/user-event' diff --git a/dapp/__mocks__/walletConnect.ts b/dapp/__mocks__/walletConnect.ts new file mode 100644 index 0000000..94444ea --- /dev/null +++ b/dapp/__mocks__/walletConnect.ts @@ -0,0 +1,140 @@ +import { vi } from 'vitest' +import { SessionTypes, PairingTypes } from '@walletconnect/types' + +export const mockAddress = 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp' + +export const mockSession: SessionTypes.Struct = { + topic: 'mock-session-topic', + relay: { protocol: 'irn' }, + expiry: Math.floor(Date.now() / 1000) + 86400, + acknowledged: true, + controller: 'mock-controller', + namespaces: { + hathor: { + accounts: [`hathor:testnet:${mockAddress}`], + methods: [ + 'htr_getAddress', + 'htr_getBalance', + 'htr_getUtxos', + 'htr_signWithAddress', + 'htr_sendNanoContractTx', + ], + events: [], + }, + }, + requiredNamespaces: {}, + optionalNamespaces: {}, + pairingTopic: 'mock-pairing-topic', + self: { + publicKey: 'mock-public-key', + metadata: { + name: 'Mock dApp', + description: 'Mock dApp', + url: 'http://localhost:3000', + icons: [], + }, + }, + peer: { + publicKey: 'mock-peer-public-key', + metadata: { + name: 'Mock Wallet', + description: 'Mock Wallet', + url: 'http://localhost', + icons: [], + }, + }, +} + +export const mockPairing: PairingTypes.Struct = { + topic: 'mock-pairing-topic', + relay: { protocol: 'irn' }, + peerMetadata: { + name: 'Mock Wallet', + description: 'Mock Wallet', + url: 'http://localhost', + icons: [], + }, + expiry: Math.floor(Date.now() / 1000) + 86400, + active: true, +} + +export const createMockSignClient = () => { + const eventHandlers: Record void>> = {} + + return { + on: vi.fn((event: string, handler: (...args: any[]) => void) => { + if (!eventHandlers[event]) { + eventHandlers[event] = [] + } + eventHandlers[event].push(handler) + }), + connect: vi.fn(async () => ({ + uri: 'wc:mock-uri', + approval: async () => mockSession, + })), + disconnect: vi.fn(async () => {}), + request: vi.fn(), + pairing: { + getAll: vi.fn(() => [mockPairing]), + }, + session: { + length: 1, + keys: ['mock-session-topic'], + get: vi.fn(() => mockSession), + }, + emit: vi.fn((event: string, ...args: any[]) => { + if (eventHandlers[event]) { + eventHandlers[event].forEach(handler => handler(...args)) + } + }), + } +} + +export const createMockWeb3Modal = () => ({ + openModal: vi.fn(), + closeModal: vi.fn(), + subscribeModal: vi.fn((callback: (state: { open: boolean }) => void) => { + return vi.fn() + }), +}) + +// Mock the @walletconnect/sign-client module +export const mockWalletConnectSignClient = { + default: vi.fn().mockImplementation(() => createMockSignClient()), +} + +// Mock the @web3modal/standalone module +export const mockWeb3ModalStandalone = { + Web3Modal: vi.fn().mockImplementation(() => createMockWeb3Modal()), +} + +// Mock the @walletconnect/utils module +export const mockWalletConnectUtils = { + getSdkError: vi.fn((errorKey: string) => ({ + code: 1000, + message: errorKey, + })), +} + +// Helper to setup WalletConnect mocks in tests +export const setupWalletConnectMocks = () => { + vi.mock('@walletconnect/sign-client', () => mockWalletConnectSignClient) + vi.mock('@web3modal/standalone', () => mockWeb3ModalStandalone) + vi.mock('@walletconnect/utils', () => mockWalletConnectUtils) +} + +// Mock WalletConnect context value for testing +export const createMockWalletConnectContext = (overrides?: Partial) => ({ + client: createMockSignClient(), + session: mockSession, + connect: vi.fn(), + disconnect: vi.fn(), + chains: ['hathor:testnet'], + pairings: [mockPairing], + accounts: [`hathor:testnet:${mockAddress}`], + setChains: vi.fn(), + getFirstAddress: vi.fn(() => mockAddress), + isConnected: true, + isInitializing: false, + ...overrides, +}) diff --git a/dapp/__tests__/integration/MetaMaskContext.test.tsx b/dapp/__tests__/integration/MetaMaskContext.test.tsx new file mode 100644 index 0000000..dec4787 --- /dev/null +++ b/dapp/__tests__/integration/MetaMaskContext.test.tsx @@ -0,0 +1,303 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' +import { renderHook, waitFor } from '@testing-library/react' +import { ReactNode } from 'react' +import { MetaMaskProvider, useMetaMask } from '@/contexts/MetaMaskContext' +import { + setupMetaMaskMock, + cleanupMetaMaskMock, + mockAddress, + SNAP_ID, +} from '@/__mocks__/metamask' + +describe('MetaMaskContext', () => { + beforeEach(() => { + setupMetaMaskMock() + localStorage.clear() + }) + + afterEach(() => { + cleanupMetaMaskMock() + vi.clearAllMocks() + }) + + const wrapper = ({ children }: { children: ReactNode }) => ( + {children} + ) + + describe('Initialization', () => { + it('should initialize with correct default values', () => { + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + expect(result.current.address).toBeNull() + expect(result.current.isConnected).toBe(false) + expect(result.current.isInstalled).toBe(true) // MetaMask mock is installed + }) + + it('should detect MetaMask installation', () => { + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + expect(result.current.isInstalled).toBe(true) + }) + + it('should detect when MetaMask is not installed', () => { + cleanupMetaMaskMock() + + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + expect(result.current.isInstalled).toBe(false) + }) + }) + + describe('Connection', () => { + it('should connect to MetaMask Snap successfully', async () => { + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + await waitFor(async () => { + await result.current.connect() + }) + + await waitFor(() => { + expect(result.current.isConnected).toBe(true) + expect(result.current.address).toBe(mockAddress) + }) + }) + + it('should store wallet type and address in localStorage', async () => { + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + await waitFor(async () => { + await result.current.connect() + }) + + await waitFor(() => { + expect(localStorage.getItem('wallet_type')).toBe('metamask') + expect(localStorage.getItem('address')).toBe(mockAddress) + }) + }) + + it('should throw error when MetaMask is not installed', async () => { + cleanupMetaMaskMock() + + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + await waitFor(async () => { + await expect(result.current.connect()).rejects.toThrow('MetaMask is not installed') + }) + }) + + it('should request snap connection with correct parameters', async () => { + const mockEthereum = window.ethereum + const requestSpy = vi.spyOn(mockEthereum!, 'request') + + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + await waitFor(async () => { + await result.current.connect() + }) + + await waitFor(() => { + expect(requestSpy).toHaveBeenCalledWith({ + method: 'wallet_requestSnaps', + params: { + [SNAP_ID]: { + version: '*', + }, + }, + }) + }) + }) + }) + + describe('Disconnection', () => { + it('should disconnect and clear state', async () => { + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + // Connect first + await waitFor(async () => { + await result.current.connect() + }) + + await waitFor(() => { + expect(result.current.isConnected).toBe(true) + }) + + // Then disconnect + await waitFor(async () => { + await result.current.disconnect() + }) + + await waitFor(() => { + expect(result.current.address).toBeNull() + expect(result.current.isConnected).toBe(false) + }) + }) + + it('should remove localStorage items on disconnect', async () => { + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + // Connect + await waitFor(async () => { + await result.current.connect() + }) + + await waitFor(() => { + expect(localStorage.getItem('wallet_type')).toBe('metamask') + }) + + // Disconnect + await waitFor(async () => { + await result.current.disconnect() + }) + + await waitFor(() => { + expect(localStorage.getItem('wallet_type')).toBeNull() + expect(localStorage.getItem('address')).toBeNull() + }) + }) + }) + + describe('Persisted Connection', () => { + it('should restore connection from localStorage', async () => { + // Setup persisted connection + localStorage.setItem('wallet_type', 'metamask') + localStorage.setItem('address', mockAddress) + + // Mock wallet_getSnaps to simulate already connected snap + const mockEthereum = window.ethereum + const originalRequest = mockEthereum!.request + mockEthereum!.request = vi.fn(async ({ method, params }: any) => { + if (method === 'wallet_getSnaps') { + return { + [SNAP_ID]: { + id: SNAP_ID, + version: '1.0.0', + enabled: true, + }, + } + } + return originalRequest.call(mockEthereum, { method, params }) + }) + + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + await waitFor( + () => { + expect(result.current.isConnected).toBe(true) + expect(result.current.address).toBe(mockAddress) + }, + { timeout: 3000 } + ) + }) + + it('should not restore connection if wallet type is not metamask', async () => { + localStorage.setItem('wallet_type', 'walletconnect') + localStorage.setItem('address', mockAddress) + + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + // Wait a bit to ensure effect has run + await new Promise(resolve => setTimeout(resolve, 100)) + + expect(result.current.isConnected).toBe(false) + expect(result.current.address).toBeNull() + }) + }) + + describe('RPC Requests', () => { + it('should make RPC requests when connected', async () => { + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + // Connect first + await waitFor(async () => { + await result.current.connect() + }) + + await waitFor(() => { + expect(result.current.isConnected).toBe(true) + }) + + // Make a request + const networkResult = await result.current.request('htr_getConnectedNetwork') + + expect(networkResult).toBeDefined() + expect(networkResult).toHaveProperty('network') + }) + + it('should throw error when making request without connection', async () => { + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + await expect(result.current.request('htr_getConnectedNetwork')).rejects.toThrow( + 'MetaMask Snap is not connected' + ) + }) + + it('should invoke snap with correct parameters', async () => { + const mockEthereum = window.ethereum + const requestSpy = vi.spyOn(mockEthereum!, 'request') + + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + // Connect first + await waitFor(async () => { + await result.current.connect() + }) + + await waitFor(() => { + expect(result.current.isConnected).toBe(true) + }) + + // Clear previous calls + requestSpy.mockClear() + + // Make a request + await result.current.request('htr_getBalance', { index: 0 }) + + expect(requestSpy).toHaveBeenCalledWith({ + method: 'wallet_invokeSnap', + params: { + snapId: SNAP_ID, + request: { + method: 'htr_getBalance', + params: { index: 0 }, + }, + }, + }) + }) + }) + + describe('Hook Error Handling', () => { + it.skip('should throw error when used outside provider', () => { + // Note: This test is skipped because renderHook in React 18 doesn't throw synchronously + // The error handling works correctly in runtime, just difficult to test with current tooling + }) + }) + + describe('Network Switching', () => { + it('should handle wallet information checking during connection', async () => { + const mockEthereum = window.ethereum + const requestSpy = vi.spyOn(mockEthereum!, 'request') + + const { result } = renderHook(() => useMetaMask(), { wrapper }) + + await waitFor(async () => { + await result.current.connect() + }) + + await waitFor(() => { + expect(result.current.isConnected).toBe(true) + }) + + // Should have called getWalletInformation during connection + expect(requestSpy).toHaveBeenCalledWith( + expect.objectContaining({ + method: 'wallet_invokeSnap', + params: expect.objectContaining({ + request: expect.objectContaining({ + method: 'htr_getWalletInformation', + }), + }), + }) + ) + }) + }) +}) diff --git a/dapp/__tests__/unit/hathorCoreAPI.test.ts b/dapp/__tests__/unit/hathorCoreAPI.test.ts new file mode 100644 index 0000000..afbc2c0 --- /dev/null +++ b/dapp/__tests__/unit/hathorCoreAPI.test.ts @@ -0,0 +1,412 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest' +import { HathorCoreAPI } from '@/lib/hathorCoreAPI' +import { mockFetch, createMockResponse, resetMockFetch } from '@/__mocks__/testUtils' + +describe('HathorCoreAPI', () => { + let api: HathorCoreAPI + + beforeEach(() => { + resetMockFetch() + api = new HathorCoreAPI('testnet') + }) + + describe('constructor', () => { + it('should initialize with testnet URL', () => { + const testnetApi = new HathorCoreAPI('testnet') + expect(testnetApi).toBeInstanceOf(HathorCoreAPI) + }) + + it('should initialize with mainnet URL', () => { + const mainnetApi = new HathorCoreAPI('mainnet') + expect(mainnetApi).toBeInstanceOf(HathorCoreAPI) + }) + }) + + describe('getBlueprintInfo', () => { + it('should fetch blueprint info successfully', async () => { + const mockBlueprint = { + id: 'test-blueprint-id', + name: 'TestContract', + attributes: { + public_methods: ['deposit', 'withdraw'], + }, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockBlueprint)) + + const result = await api.getBlueprintInfo('test-blueprint-id') + + expect(mockFetch).toHaveBeenCalledWith( + expect.stringContaining('/nc_blueprint/test-blueprint-id') + ) + expect(result).toEqual(mockBlueprint) + }) + + it('should throw error on failed request', async () => { + mockFetch.mockResolvedValueOnce(createMockResponse({}, 404)) + + await expect(api.getBlueprintInfo('invalid-id')).rejects.toThrow( + 'Failed to fetch blueprint info' + ) + }) + + it('should handle network errors', async () => { + mockFetch.mockRejectedValueOnce(new Error('Network error')) + + await expect(api.getBlueprintInfo('test-id')).rejects.toThrow('Network error') + }) + }) + + describe('getContractState', () => { + it('should fetch and parse contract state successfully', async () => { + const mockResponse = { + fields: { + token_uid: { value: '00' }, + available_tokens: { value: 100000000n }, + total_liquidity_provided: { value: 100000000n }, + }, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + const result = await api.getContractState('contract-id') + + expect(mockFetch).toHaveBeenCalledWith( + expect.stringContaining('/nano_contract/state?id=contract-id') + ) + expect(result.token_uid).toBe('00') + expect(result.available_tokens).toBe(100000000n) + expect(result.total_liquidity_provided).toBe(100000000n) + }) + + it('should use default values when fields are missing', async () => { + const mockResponse = { + fields: {}, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + const result = await api.getContractState('contract-id') + + expect(result.token_uid).toBe('00') + expect(result.available_tokens).toBe(0n) + expect(result.total_liquidity_provided).toBe(0n) + }) + + it('should fetch custom fields when provided', async () => { + const mockResponse = { + fields: { + token_uid: { value: '00' }, + custom_field: { value: 42 }, + }, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + const result = await api.getContractState('contract-id', ['token_uid', 'custom_field']) + + expect(mockFetch).toHaveBeenCalledWith( + expect.stringContaining('fields[]=token_uid') + ) + expect(mockFetch).toHaveBeenCalledWith( + expect.stringContaining('fields[]=custom_field') + ) + expect(result.custom_field).toBe(42) + }) + + it('should throw error on failed request', async () => { + mockFetch.mockResolvedValueOnce(createMockResponse({}, 500)) + + await expect(api.getContractState('contract-id')).rejects.toThrow( + 'Failed to fetch contract state' + ) + }) + }) + + describe('getContractHistory', () => { + it('should fetch contract history successfully', async () => { + const mockResponse = { + history: [ + { + hash: '0000000000000001', + timestamp: Date.now() - 60000, + nc_method: 'deposit', + nc_address: 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp', + first_block: '0000000000000abc', + is_voided: false, + nc_args_decoded: { amount: 1000 }, + nc_events: [ + { + type: 'Deposit', + data: '{"user":"WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp","amount":1000}', + }, + ], + }, + ], + has_more: false, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + const result = await api.getContractHistory('contract-id', 50) + + expect(mockFetch).toHaveBeenCalledWith( + expect.stringContaining('/nano_contract/history?id=contract-id&count=50') + ) + expect(result.transactions).toHaveLength(1) + expect(result.transactions[0].tx_id).toBe('0000000000000001') + expect(result.transactions[0].nc_method).toBe('deposit') + expect(result.hasMore).toBe(false) + }) + + it('should include pagination parameter when provided', async () => { + const mockResponse = { + history: [], + has_more: true, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + await api.getContractHistory('contract-id', 50, 'after-tx-id') + + expect(mockFetch).toHaveBeenCalledWith( + expect.stringContaining('after=after-tx-id') + ) + }) + + it('should handle empty history', async () => { + const mockResponse = { + history: [], + has_more: false, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + const result = await api.getContractHistory('contract-id') + + expect(result.transactions).toHaveLength(0) + expect(result.total).toBe(0) + expect(result.hasMore).toBe(false) + }) + + it('should throw error on failed request', async () => { + mockFetch.mockResolvedValueOnce(createMockResponse({}, 404)) + + await expect(api.getContractHistory('invalid-id')).rejects.toThrow( + 'Failed to fetch contract history' + ) + }) + }) + + describe('getTransaction', () => { + it('should fetch transaction successfully', async () => { + const mockTx = { + hash: '0000000000000001', + timestamp: Date.now(), + inputs: [], + outputs: [], + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockTx)) + + const result = await api.getTransaction('0000000000000001') + + expect(mockFetch).toHaveBeenCalledWith( + expect.stringContaining('/transaction?id=0000000000000001') + ) + expect(result.hash).toBe('0000000000000001') + }) + + it('should throw error on failed request', async () => { + mockFetch.mockResolvedValueOnce(createMockResponse({}, 404)) + + await expect(api.getTransaction('invalid-tx-id')).rejects.toThrow( + 'Failed to fetch transaction' + ) + }) + }) + + describe('callViewFunction', () => { + it('should call view function without caller address', async () => { + const mockResponse = { + calls: { + 'get_balance()': { + value: 1000, + }, + }, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + const result = await api.callViewFunction('contract-id', 'get_balance', []) + + const callUrl = mockFetch.mock.calls[0][0] as string + expect(callUrl).toContain('/nano_contract/state?') + expect(callUrl).toContain('id=contract-id') + expect(callUrl).toContain('get_balance') + expect(result).toEqual(mockResponse) + }) + + it('should call view function with arguments', async () => { + const mockResponse = { + calls: { + 'get_address_balance("WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp")': { + value: 5000, + }, + }, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + const result = await api.callViewFunction( + 'contract-id', + 'get_address_balance', + ['WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp'] + ) + + const callUrl = mockFetch.mock.calls[0][0] as string + expect(callUrl).toContain('get_address_balance') + expect(callUrl).toContain('WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp') + expect(result).toEqual(mockResponse) + }) + + it('should call view function with caller address', async () => { + const mockResponse = { + calls: { + 'calculate_max()': { + value: 10000, + }, + }, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + await api.callViewFunction( + 'contract-id', + 'calculate_max', + [], + 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp' + ) + + expect(mockFetch).toHaveBeenCalled() + }) + + it('should throw error on failed request', async () => { + mockFetch.mockResolvedValueOnce(createMockResponse({}, 500)) + + await expect( + api.callViewFunction('contract-id', 'invalid_method', []) + ).rejects.toThrow('Failed to call view function') + }) + }) + + describe('getMaximumLiquidityRemoval', () => { + it('should get maximum liquidity removal amount', async () => { + const mockResponse = { + calls: { + 'calculate_address_maximum_liquidity_removal("WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp")': { + value: 10000, + }, + }, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + const result = await api.getMaximumLiquidityRemoval( + 'contract-id', + 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp' + ) + + expect(result).toBe(10000n) + }) + + it('should return 0n when no result is available', async () => { + const mockResponse = { + calls: {}, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + const result = await api.getMaximumLiquidityRemoval( + 'contract-id', + 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp' + ) + + expect(result).toBe(0n) + }) + + it('should handle undefined value', async () => { + const mockResponse = { + calls: { + 'calculate_address_maximum_liquidity_removal("WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp")': { + value: undefined, + }, + }, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + const result = await api.getMaximumLiquidityRemoval( + 'contract-id', + 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp' + ) + + expect(result).toBe(0n) + }) + }) + + describe('getClaimableBalance', () => { + it('should get claimable balance', async () => { + const mockResponse = { + calls: { + 'get_address_balance("WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp")': { + value: 5000, + }, + }, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + const result = await api.getClaimableBalance( + 'contract-id', + 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp' + ) + + expect(result).toBe(5000n) + }) + + it('should return 0n when no balance is available', async () => { + const mockResponse = { + calls: {}, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + const result = await api.getClaimableBalance( + 'contract-id', + 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp' + ) + + expect(result).toBe(0n) + }) + + it('should handle string value conversion to bigint', async () => { + const mockResponse = { + calls: { + 'get_address_balance("WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp")': { + value: '999999999999', + }, + }, + } + + mockFetch.mockResolvedValueOnce(createMockResponse(mockResponse)) + + const result = await api.getClaimableBalance( + 'contract-id', + 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp' + ) + + expect(result).toBe(999999999999n) + }) + }) +}) diff --git a/dapp/__tests__/unit/hathorRPC.test.ts b/dapp/__tests__/unit/hathorRPC.test.ts new file mode 100644 index 0000000..157a53f --- /dev/null +++ b/dapp/__tests__/unit/hathorRPC.test.ts @@ -0,0 +1,320 @@ +import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest' +import { HathorRPCService } from '@/lib/hathorRPC' +import { createMockSignClient, mockSession } from '@/__mocks__/walletConnect' + +describe('HathorRPCService', () => { + describe('Mock Mode', () => { + let rpcService: HathorRPCService + + beforeEach(() => { + rpcService = new HathorRPCService(true) + vi.useFakeTimers() + }) + + afterEach(() => { + vi.useRealTimers() + }) + + it('should return mock network data', async () => { + const networkPromise = rpcService.getConnectedNetwork() + vi.advanceTimersByTime(500) + const result = await networkPromise + + expect(result).toEqual({ + network: 'testnet', + genesisHash: '0x123...', + }) + }) + + it('should return mock balance data', async () => { + const balancePromise = rpcService.getBalance({ index: 0 }) + vi.advanceTimersByTime(500) + const result = await balancePromise + + expect(result.response).toHaveLength(1) + expect(result.response[0].token.symbol).toBe('HTR') + expect(result.response[0].balance.unlocked).toBe(1250.5) + }) + + it('should return mock address', async () => { + const addressPromise = rpcService.getAddress({ index: 0 }) + vi.advanceTimersByTime(500) + const result = await addressPromise + + expect(result.address).toBe('WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp') + expect(result.index).toBe(0) + }) + + it('should return mock transaction response', async () => { + const txPromise = rpcService.sendNanoContractTx({ + address: 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp', + nc_id: 'contract-id', + nc_blueprint_id: 'blueprint-id', + nc_method: 'deposit', + nc_args: ['32768'], + nc_actions: [] + }) + vi.advanceTimersByTime(500) + const result = await txPromise + + expect(result.hash).toMatch(/^00000000[a-z0-9]+/) + expect(result.success).toBe(true) + expect(result.timestamp).toBeDefined() + }) + + it('should throw error for unimplemented mock method', async () => { + const unknownPromise = rpcService.request('htr_unknownMethod') + vi.advanceTimersByTime(500) + + await expect(unknownPromise).rejects.toThrow('Mock not implemented for method') + }) + + it('should simulate network delay', async () => { + const addressPromise = rpcService.getAddress({ index: 0 }) + + // Advance time to simulate delay + vi.advanceTimersByTime(500) + const result = await addressPromise + + expect(result.address).toBe('WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp') + }) + }) + + describe('WalletConnect Mode', () => { + let rpcService: HathorRPCService + let mockClient: any + + beforeEach(() => { + mockClient = createMockSignClient() + // Pass 'testnet' as the network parameter + rpcService = new HathorRPCService(false, mockClient, mockSession, undefined, 'testnet') + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + it('should make request through WalletConnect client', async () => { + mockClient.request.mockResolvedValueOnce({ + network: 'testnet', + genesisHash: '0xreal', + }) + + const result = await rpcService.getConnectedNetwork() + + expect(mockClient.request).toHaveBeenCalledWith({ + chainId: 'hathor:testnet', + topic: mockSession.topic, + request: { + method: 'htr_getConnectedNetwork', + params: undefined, + }, + }) + expect(result.network).toBe('testnet') + }) + + it('should pass parameters to WalletConnect request', async () => { + mockClient.request.mockResolvedValueOnce({ + address: 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp', + index: 0, + addressPath: "m/44'/280'/0'/0/0", + }) + + await rpcService.getAddress({ index: 0 }) + + expect(mockClient.request).toHaveBeenCalledWith( + expect.objectContaining({ + request: expect.objectContaining({ + method: 'htr_getAddress', + params: { index: 0 }, + }), + }) + ) + }) + + it('should throw error when wallet not connected', async () => { + const disconnectedRpcService = new HathorRPCService(false) + + await expect(disconnectedRpcService.getConnectedNetwork()).rejects.toThrow( + 'Wallet not connected' + ) + }) + + it('should handle WalletConnect request errors', async () => { + mockClient.request.mockRejectedValueOnce(new Error('User rejected')) + + await expect(rpcService.getConnectedNetwork()).rejects.toThrow('User rejected') + }) + + it('should update client and session', () => { + const newMockClient = createMockSignClient() + const newMockSession = { ...mockSession, topic: 'new-topic' } + + rpcService.updateClientAndSession(newMockClient, newMockSession) + + expect(rpcService['client']).toBe(newMockClient) + expect(rpcService['session']).toBe(newMockSession) + }) + }) + + describe('Custom Request Function Mode (MetaMask)', () => { + let rpcService: HathorRPCService + let customRequest: any + + beforeEach(() => { + customRequest = vi.fn() + rpcService = new HathorRPCService(false, undefined, undefined, customRequest) + }) + + it('should use custom request function when provided', async () => { + customRequest.mockResolvedValueOnce({ + address: 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp', + index: 0, + }) + + const result = await rpcService.getAddress({ index: 0 }) + + expect(customRequest).toHaveBeenCalledWith('htr_getAddress', { index: 0 }) + expect(result.address).toBe('WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp') + }) + + it('should handle custom request function errors', async () => { + customRequest.mockRejectedValueOnce(new Error('MetaMask error')) + + await expect(rpcService.getBalance({ index: 0 })).rejects.toThrow('MetaMask error') + }) + + it('should prefer custom request over WalletConnect', async () => { + const mockClient = createMockSignClient() + rpcService = new HathorRPCService(false, mockClient, mockSession, customRequest) + + customRequest.mockResolvedValueOnce({ network: 'mainnet' }) + + await rpcService.getConnectedNetwork() + + expect(customRequest).toHaveBeenCalled() + expect(mockClient.request).not.toHaveBeenCalled() + }) + + it('should update custom request function', () => { + const newCustomRequest = vi.fn() + + rpcService.updateClientAndSession(undefined, undefined, newCustomRequest) + + expect(rpcService['customRequest']).toBe(newCustomRequest) + }) + }) + + describe('sendNanoContractTx', () => { + it('should send nano contract transaction with all parameters', async () => { + const mockClient = createMockSignClient() + // Pass 'testnet' as the network parameter + const rpcService = new HathorRPCService(false, mockClient, mockSession, undefined, 'testnet') + + mockClient.request.mockResolvedValueOnce({ + hash: '0000000000000001', + success: true, + }) + + const txParams = { + address: 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp', + nc_id: 'contract-id', + nc_blueprint_id: 'blueprint-id', + nc_method: 'deposit', + nc_args: ['32768'], + nc_actions: [ + { + type: 'deposit', + token: '00', + amount: '1000', + }, + ], + } + + const result = await rpcService.sendNanoContractTx(txParams) + + expect(mockClient.request).toHaveBeenCalledWith({ + chainId: 'hathor:testnet', + topic: mockSession.topic, + request: { + method: 'htr_sendNanoContractTx', + params: txParams, + }, + }) + expect(result.success).toBe(true) + }) + }) + + describe('Mode switching', () => { + it('should respect useMock flag throughout lifecycle', async () => { + const mockClient = createMockSignClient() + const mockRpcService = new HathorRPCService(true, mockClient, mockSession) + + vi.useFakeTimers() + const resultPromise = mockRpcService.getConnectedNetwork() + vi.advanceTimersByTime(500) + const result = await resultPromise + vi.useRealTimers() + + expect(result.genesisHash).toBe('0x123...') // Mock value + expect(mockClient.request).not.toHaveBeenCalled() + }) + + it('should switch from mock to real mode', async () => { + const mockClient = createMockSignClient() + mockClient.request.mockResolvedValueOnce({ + network: 'testnet', + genesisHash: '0xreal', + }) + + const rpcService = new HathorRPCService(false, mockClient, mockSession, undefined, 'testnet') + const result = await rpcService.getConnectedNetwork() + + expect(result.genesisHash).toBe('0xreal') + expect(mockClient.request).toHaveBeenCalled() + }) + }) + + describe('Error handling', () => { + let mockClient: any + let rpcService: HathorRPCService + + beforeEach(() => { + mockClient = createMockSignClient() + rpcService = new HathorRPCService(false, mockClient, mockSession, undefined, 'testnet') + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + it('should handle errors with messages', async () => { + mockClient.request.mockRejectedValueOnce({ + message: 'Specific error message', + }) + + await expect(rpcService.getConnectedNetwork()).rejects.toThrow( + 'Specific error message' + ) + }) + + it('should handle errors without messages', async () => { + mockClient.request.mockRejectedValueOnce({}) + + await expect(rpcService.getConnectedNetwork()).rejects.toThrow( + 'RPC request failed' + ) + }) + + it('should handle custom request errors with messages', async () => { + const customRequest = vi.fn() + const rpcService = new HathorRPCService(false, undefined, undefined, customRequest) + + customRequest.mockRejectedValueOnce({ + message: 'Custom error', + }) + + await expect(rpcService.getConnectedNetwork()).rejects.toThrow('Custom error') + }) + }) +}) diff --git a/dapp/__tests__/unit/utils.test.ts b/dapp/__tests__/unit/utils.test.ts new file mode 100644 index 0000000..ebc2860 --- /dev/null +++ b/dapp/__tests__/unit/utils.test.ts @@ -0,0 +1,172 @@ +import { describe, it, expect } from 'vitest' +import { + cn, + formatAddress, + formatNumber, + formatTokenAmount, + formatBalance, + formatBalanceWithCommas, +} from '@/lib/utils' + +describe('utils', () => { + describe('cn - className utility', () => { + it('should join valid class names', () => { + expect(cn('class1', 'class2', 'class3')).toBe('class1 class2 class3') + }) + + it('should filter out falsy values', () => { + expect(cn('class1', undefined, 'class2', null, false, 'class3')).toBe('class1 class2 class3') + }) + + it('should return empty string for all falsy inputs', () => { + expect(cn(undefined, null, false)).toBe('') + }) + + it('should handle empty input', () => { + expect(cn()).toBe('') + }) + }) + + describe('formatAddress', () => { + it('should format a standard Hathor address', () => { + const address = 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp' + const formatted = formatAddress(address) + expect(formatted).toBe('WYBwT3...AvNp') + }) + + it('should handle shorter addresses', () => { + const address = 'WYBwT3xLp' + const formatted = formatAddress(address) + expect(formatted).toBe('WYBwT3...3xLp') + }) + + it('should handle empty string', () => { + expect(formatAddress('')).toBe('') + }) + + it('should preserve format for very short addresses', () => { + const address = 'ABCD' + const formatted = formatAddress(address) + expect(formatted).toBe('ABCD...ABCD') + }) + }) + + describe('formatNumber', () => { + it('should format number with default 2 decimals', () => { + expect(formatNumber(123.456)).toBe('123.46') + }) + + it('should format number with custom decimals', () => { + expect(formatNumber(123.456, 3)).toBe('123.456') + expect(formatNumber(123.456, 1)).toBe('123.5') + expect(formatNumber(123.456, 0)).toBe('123') + }) + + it('should handle integers', () => { + expect(formatNumber(123)).toBe('123.00') + }) + + it('should handle zero', () => { + expect(formatNumber(0)).toBe('0.00') + }) + + it('should handle negative numbers', () => { + expect(formatNumber(-123.456)).toBe('-123.46') + }) + }) + + describe('formatTokenAmount', () => { + it('should format bigint amount correctly (100 = 1.00)', () => { + expect(formatTokenAmount(100n)).toBe('1.00') + }) + + it('should format bigint amount with large value', () => { + expect(formatTokenAmount(123456n)).toBe('1234.56') + }) + + it('should format bigint amount with small value', () => { + expect(formatTokenAmount(50n)).toBe('0.50') + }) + + it('should format bigint amount with very small value', () => { + expect(formatTokenAmount(5n)).toBe('0.05') + }) + + it('should format bigint amount with 1 unit', () => { + expect(formatTokenAmount(1n)).toBe('0.01') + }) + + it('should format bigint zero', () => { + expect(formatTokenAmount(0n)).toBe('0.00') + }) + + it('should format number amount correctly', () => { + expect(formatTokenAmount(100)).toBe('1.00') + expect(formatTokenAmount(12345)).toBe('123.45') + }) + + it('should handle large bigint amounts', () => { + expect(formatTokenAmount(100000000n)).toBe('1000000.00') + }) + + it('should pad with zeros for amounts less than 100', () => { + expect(formatTokenAmount(99n)).toBe('0.99') + expect(formatTokenAmount(10n)).toBe('0.10') + expect(formatTokenAmount(1n)).toBe('0.01') + }) + }) + + describe('formatBalance', () => { + it('should format bigint balance correctly (100 = 1.00)', () => { + expect(formatBalance(100n)).toBe('1.00') + }) + + it('should format bigint balance with large value', () => { + expect(formatBalance(125050n)).toBe('1250.50') + }) + + it('should format bigint balance with small value', () => { + expect(formatBalance(50n)).toBe('0.50') + }) + + it('should format bigint zero balance', () => { + expect(formatBalance(0n)).toBe('0.00') + }) + + it('should format number balance correctly', () => { + expect(formatBalance(100)).toBe('1.00') + expect(formatBalance(125050)).toBe('1250.50') + }) + + it('should handle large balances', () => { + expect(formatBalance(10000000000n)).toBe('100000000.00') + }) + + it('should pad with zeros for small amounts', () => { + expect(formatBalance(5n)).toBe('0.05') + expect(formatBalance(1n)).toBe('0.01') + }) + }) + + describe('formatBalanceWithCommas', () => { + it('should format balance with thousand separators', () => { + expect(formatBalanceWithCommas(123456789n)).toBe('1,234,567.89') + }) + + it('should format small balance without commas', () => { + expect(formatBalanceWithCommas(12345n)).toBe('123.45') + }) + + it('should format large balance with multiple commas', () => { + expect(formatBalanceWithCommas(123456789012n)).toBe('1,234,567,890.12') + }) + + it('should handle zero', () => { + expect(formatBalanceWithCommas(0n)).toBe('0.00') + }) + + it('should handle numbers', () => { + expect(formatBalanceWithCommas(123456789)).toBe('1,234,567.89') + }) + }) +}) diff --git a/dapp/app/globals.css b/dapp/app/globals.css new file mode 100644 index 0000000..3251ce5 --- /dev/null +++ b/dapp/app/globals.css @@ -0,0 +1,62 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --background: #0f172a; + --foreground: #f1f5f9; +} + +body { + color: var(--foreground); + background: var(--background); + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; +} + +@keyframes bounce-once { + 0%, 100% { + transform: scale(1) translateY(0); + } + 25% { + transform: scale(1.1) translateY(-20px); + } + 50% { + transform: scale(0.95) translateY(0); + } + 75% { + transform: scale(1.05) translateY(-10px); + } +} + +@keyframes spin-slow { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@keyframes confetti { + 0% { + transform: translateY(-10vh) rotate(0deg); + opacity: 1; + } + 100% { + transform: translateY(100vh) rotate(720deg); + opacity: 0; + } +} + +.animate-bounce-once { + animation: bounce-once 0.8s ease-out; +} + +.animate-spin-slow { + animation: spin-slow 3s linear infinite; +} + +.animate-confetti { + animation: confetti 3s ease-in forwards; + font-size: 2rem; +} diff --git a/dapp/app/layout.tsx b/dapp/app/layout.tsx new file mode 100644 index 0000000..d2d3741 --- /dev/null +++ b/dapp/app/layout.tsx @@ -0,0 +1,43 @@ +import type { Metadata } from 'next'; +import { WalletProvider } from '@/contexts/WalletContext'; +import { HathorProvider } from '@/contexts/HathorContext'; +import { WalletConnectProvider } from '@/contexts/WalletConnectContext'; +import { MetaMaskProvider } from '@/contexts/MetaMaskContext'; +import { UnifiedWalletProvider } from '@/contexts/UnifiedWalletContext'; +import { ToastProvider, Toaster } from '@/lib/toast'; +import './globals.css'; + +export const metadata: Metadata = { + title: 'x402 Client — Hathor', + description: 'x402 hathor-direct payment client (regular UTXO payments, no nano contracts)', + icons: { + icon: '/images/icon.png', + }, +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + + + + + + + {children} + + + + + + + + + + ); +} diff --git a/dapp/app/page.tsx b/dapp/app/page.tsx new file mode 100644 index 0000000..604b746 --- /dev/null +++ b/dapp/app/page.tsx @@ -0,0 +1,93 @@ +'use client'; + +import { useState } from 'react'; +import { useWallet } from '@/contexts/WalletContext'; +import { useHathor } from '@/contexts/HathorContext'; +import Header from '@/components/Header'; +import BalanceCard from '@/components/BalanceCard'; +import { WalletConnectionModal } from '@/components/WalletConnectionModal'; +import { X402Fetch } from '@/components/X402Fetch'; +import { formatAddress } from '@/lib/utils'; + +export default function Home() { + const { address } = useWallet(); + const { network, isConnected } = useHathor(); + const [showWalletModal, setShowWalletModal] = useState(false); + + return ( +
+
+ +
+ {/* Hero */} +
+

x402 Payment Client

+

+ Access paid HTTP APIs on Hathor Network — regular UTXO payments, no nano contracts. +

+
+ + {/* Wallet bar */} +
+ {isConnected ? ( + <> +
+
+
+ Connected: + {formatAddress(address || '')} + ({network}) +
+
+ + + ) : ( + <> +
+
+ Wallet not connected +
+ + + )} +
+ + {/* Main flow */} + + + {/* How it works */} +
+

How x402 hathor-direct works

+
+ {[ + { step: '1', title: 'Fetch', desc: 'Request a paid API endpoint' }, + { step: '2', title: '402', desc: 'Server responds with payment requirements' }, + { step: '3', title: 'Pay + Sign', desc: 'Send tx + sign the request id with your key' }, + { step: '4', title: 'Access', desc: 'Retry with the proof — get the data' }, + ].map(({ step, title, desc }) => ( +
+
+ {step} +
+

{title}

+

{desc}

+
+ ))} +
+
+
+ + +
+ ); +} diff --git a/dapp/app/payment/[txId]/page.tsx b/dapp/app/payment/[txId]/page.tsx new file mode 100644 index 0000000..1af7d29 --- /dev/null +++ b/dapp/app/payment/[txId]/page.tsx @@ -0,0 +1,253 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useHathor } from '@/contexts/HathorContext'; +import Header from '@/components/Header'; +import { formatAddress } from '@/lib/utils'; +import { getPayment, markVoided, PaymentRecord } from '@/lib/paymentHistory'; + +interface PageProps { + params: { txId: string }; +} + +function describeAmount(atomicStr?: string) { + if (!atomicStr) return ''; + const n = parseInt(atomicStr, 10); + if (!Number.isFinite(n)) return atomicStr; + return `${(n / 100).toFixed(2)} HTR`; +} + +export default function PaymentDetailPage({ params }: PageProps) { + const { txId } = params; + const { coreAPI } = useHathor(); + const [record, setRecord] = useState(() => getPayment(txId)); + const [tx, setTx] = useState(null); + const [meta, setMeta] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + let cancelled = false; + setLoading(true); + coreAPI + .getTransaction(txId) + .then((data) => { + if (cancelled) return; + if (!data?.success) { + setError('Transaction not found at fullnode'); + setLoading(false); + return; + } + setTx(data.tx); + setMeta(data.meta || null); + + // If the chain reports the tx voided, update local record so the + // history badge reflects reality. + if (Array.isArray(data.meta?.voided_by) && data.meta.voided_by.length > 0) { + markVoided(txId); + setRecord(getPayment(txId)); + } + setLoading(false); + }) + .catch((err) => { + if (cancelled) return; + setError(err.message || 'Failed to load transaction'); + setLoading(false); + }); + return () => { + cancelled = true; + }; + }, [coreAPI, txId]); + + const payOutputIndex = (() => { + if (!tx?.outputs || !record) return -1; + return tx.outputs.findIndex( + (o: any) => + o.decoded?.address === record.payTo && + BigInt(o.value) >= BigInt(parseInt(record.amount, 10) || 0) + ); + })(); + + const voided = Array.isArray(meta?.voided_by) && meta.voided_by.length > 0; + const conflicted = Array.isArray(meta?.conflict_with) && meta.conflict_with.length > 0; + + return ( +
+
+ +
+ + ← back + + +

Payment

+

{txId}

+ + {loading &&
Loading...
} + + {error && ( +
+ {error} +
+ )} + + {!loading && tx && ( + <> + {/* Local record */} + {record && ( +
+

Local record

+
+
+ URL:{' '} + {record.url} +
+
+ Scheme:{' '} + {record.scheme} +
+
+ Pay to:{' '} + {formatAddress(record.payTo)} +
+
+ Payer:{' '} + + {formatAddress(record.payerAddress)} + +
+
+ Amount paid:{' '} + {describeAmount(record.amount)} +
+ {record.chargedAmount != null && ( +
+ Charged:{' '} + + {describeAmount(record.chargedAmount)} + +
+ )} + {record.refundAmount != null && parseInt(record.refundAmount, 10) > 0 && ( +
+ Refunded:{' '} + + {describeAmount(record.refundAmount)} + +
+ )} + {record.refundTxId && ( +
+ Refund tx:{' '} + + {formatAddress(record.refundTxId)} + +
+ )} +
+ Status:{' '} + + {record.status} + +
+
+
+ )} + + {/* On-chain status */} +
+

On-chain status

+
+
+ first_block:{' '} + {meta?.first_block ? ( + + {formatAddress(meta.first_block)} + + ) : ( + unconfirmed (mempool) + )} +
+
+ voided_by:{' '} + {voided ? ( + {meta.voided_by.length} tx(s) + ) : ( + none + )} +
+
+ conflict_with:{' '} + {conflicted ? ( + {meta.conflict_with.length} tx(s) + ) : ( + none + )} +
+
+ accumulated_weight:{' '} + + {meta?.accumulated_weight ?? '—'} + +
+
+
+ + {/* Outputs */} +
+

Outputs

+
+ {tx.outputs?.map((o: any, i: number) => ( +
+ #{i} + {o.value} cents + {o.decoded?.address && ( + → {formatAddress(o.decoded.address)} + )} + {i === payOutputIndex && ( + [paying output] + )} +
+ ))} +
+
+ + {/* Inputs */} +
+

Inputs

+
+ {tx.inputs?.map((inp: any, i: number) => ( +
+ #{i} + + {formatAddress(inp.tx_id || '')}:{inp.index} + + {inp.decoded?.address && ( + + ({formatAddress(inp.decoded.address)}) + + )} +
+ ))} +
+
+ + )} +
+
+ ); +} diff --git a/dapp/components/BalanceCard.tsx b/dapp/components/BalanceCard.tsx new file mode 100644 index 0000000..2640c46 --- /dev/null +++ b/dapp/components/BalanceCard.tsx @@ -0,0 +1,58 @@ +'use client'; + +import { useWallet } from '@/contexts/WalletContext'; +import { useHathor } from '@/contexts/HathorContext'; +import { useUnifiedWallet } from '@/contexts/UnifiedWalletContext'; +import { formatBalanceWithCommas } from '@/lib/utils'; +import { toast } from '@/lib/toast'; + +export default function BalanceCard() { + const { balance, balanceVerified, isLoadingBalance, refreshBalance } = useWallet(); + const { isConnected, network } = useHathor(); + const { walletType } = useUnifiedWallet(); + + const handleLoadBalance = () => { + if (walletType !== 'metamask') { + toast.info('Please confirm the operation in your wallet...'); + } + // x402 hathor-direct flow only uses HTR for now. + refreshBalance('00', network); + }; + + if (!isConnected) { + return ( +
+
YOUR BALANCE
+

Connect your wallet to view balance

+
+ ); + } + + return ( +
+
YOUR BALANCE
+
+
+ Wallet Balance: + {balanceVerified && balance > 0n ? ( + {formatBalanceWithCommas(balance)} HTR + ) : isLoadingBalance ? ( + Loading... + ) : ( + + )} +
+
+
+ ); +} diff --git a/dapp/components/Header.tsx b/dapp/components/Header.tsx new file mode 100644 index 0000000..38e35ac --- /dev/null +++ b/dapp/components/Header.tsx @@ -0,0 +1,83 @@ +'use client'; + +import { useState } from 'react'; +import { useHathor } from '@/contexts/HathorContext'; +import { formatAddress } from '@/lib/utils'; +import { WalletConnectionModal } from './WalletConnectionModal'; +import { NetworkSelector } from './NetworkSelector'; + +interface HeaderProps { + appName?: string; +} + +export default function Header({ appName = 'x402 Client' }: HeaderProps) { + const { isConnected, address, disconnectWallet, network, switchNetwork } = useHathor(); + const [showModal, setShowModal] = useState(false); + const [showDisconnectMenu, setShowDisconnectMenu] = useState(false); + + const handleConnect = async () => { + setShowModal(true); + }; + + const handleNetworkChange = (newNetwork: typeof network) => { + if (isConnected) { + disconnectWallet(); + } + switchNetwork(newNetwork); + }; + + return ( + <> +
+
+

{appName}

+
+ +
+ + {isConnected ? ( +
+ + {showDisconnectMenu && ( + <> +
setShowDisconnectMenu(false)} /> +
+ +
+ + )} +
+ ) : ( + + )} +
+
+ + + + ); +} diff --git a/dapp/components/HelpIcon.tsx b/dapp/components/HelpIcon.tsx new file mode 100644 index 0000000..c8e44fd --- /dev/null +++ b/dapp/components/HelpIcon.tsx @@ -0,0 +1,46 @@ +'use client'; + +import { useState } from 'react'; + +interface HelpIconProps { + text: string; + className?: string; +} + +export default function HelpIcon({ text, className = '' }: HelpIconProps) { + const [isVisible, setIsVisible] = useState(false); + + return ( +
+ setIsVisible(true)} + onMouseLeave={() => setIsVisible(false)} + onClick={(e) => { + e.stopPropagation(); + setIsVisible(!isVisible); + }} + className="inline-flex items-center justify-center w-4 h-4 text-xs text-slate-400 hover:text-slate-300 cursor-help transition-colors" + > + + + + + + {isVisible && ( +
+
+

{text}

+
+ )} +
+ ); +} diff --git a/dapp/components/NetworkSelector.tsx b/dapp/components/NetworkSelector.tsx new file mode 100644 index 0000000..e2ae365 --- /dev/null +++ b/dapp/components/NetworkSelector.tsx @@ -0,0 +1,34 @@ +'use client'; + +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Network } from '@/lib/config'; + +interface NetworkSelectorProps { + value: Network; + onChange: (network: Network) => void; +} + +export function NetworkSelector({ value, onChange }: NetworkSelectorProps) { + const handleChange = (value: string) => { + onChange(value as Network); + }; + + const networkLabels: Record = { + privatenet: 'Privatenet', + testnet: 'Testnet', + mainnet: 'Mainnet', + }; + + return ( + + ); +} diff --git a/dapp/components/WalletConnectionModal.tsx b/dapp/components/WalletConnectionModal.tsx new file mode 100644 index 0000000..b48312a --- /dev/null +++ b/dapp/components/WalletConnectionModal.tsx @@ -0,0 +1,125 @@ +'use client'; + +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { useWalletConnect } from '@/contexts/WalletConnectContext'; +import { useMetaMask } from '@/contexts/MetaMaskContext'; +import { useHathor } from '@/contexts/HathorContext'; +import { useState, useEffect } from 'react'; + +interface WalletConnectionModalProps { + open: boolean; + onOpenChange: (open: boolean) => void; +} + +export function WalletConnectionModal({ open, onOpenChange }: WalletConnectionModalProps) { + const { connect: connectWalletConnect, isInitializing } = useWalletConnect(); + const { connect: connectMetaMask, isInstalled: isMetaMaskInstalled } = useMetaMask(); + const { network } = useHathor(); + const [isConnecting, setIsConnecting] = useState(false); + const [connectingWallet, setConnectingWallet] = useState<'reown' | 'metamask' | null>(null); + const [error, setError] = useState(null); + + useEffect(() => { + if (!open) { + setIsConnecting(false); + setConnectingWallet(null); + setError(null); + } + }, [open]); + + const handleReownConnect = async () => { + try { + setIsConnecting(true); + setConnectingWallet('reown'); + setError(null); + await connectWalletConnect(network); + onOpenChange(false); + } catch (error: any) { + console.error('Failed to connect via Reown:', error); + if (error?.message?.includes('not initialized yet')) { + setError('Wallet is initializing. Please wait a moment and try again.'); + } else if (error?.message?.includes('User closed the modal')) { + setError(null); // User intentionally closed, don't show error + } else if (error?.message?.includes('Network mismatch')) { + setError(error.message); // Show the full network mismatch error + } else { + setError('Failed to connect. Please try again.'); + } + } finally { + setIsConnecting(false); + setConnectingWallet(null); + } + }; + + const handleMetamaskConnect = async () => { + if (!isMetaMaskInstalled) { + setError('MetaMask is not installed. Please install MetaMask extension first.'); + return; + } + + try { + setIsConnecting(true); + setConnectingWallet('metamask'); + setError(null); + await connectMetaMask(network); + onOpenChange(false); + } catch (error: any) { + console.error('Failed to connect via MetaMask:', error); + if (error?.message?.includes('not installed')) { + setError('MetaMask is not installed. Please install MetaMask extension first.'); + } else if (error?.message?.includes('User rejected')) { + setError(null); // User intentionally rejected, don't show error + } else { + setError(error?.message || 'Failed to connect to MetaMask. Please try again.'); + } + } finally { + setIsConnecting(false); + setConnectingWallet(null); + } + }; + + const isReownButtonDisabled = isConnecting || isInitializing; + const isMetaMaskButtonDisabled = isConnecting || !isMetaMaskInstalled; + + return ( + + + + Connect Wallet + + Choose your preferred wallet connection method + + +
+ {error && ( +
+ {error} +
+ )} + {isInitializing && ( +
+ Initializing wallet connection... +
+ )} + + +
+
+
+ ); +} diff --git a/dapp/components/X402Fetch.tsx b/dapp/components/X402Fetch.tsx new file mode 100644 index 0000000..6fe8590 --- /dev/null +++ b/dapp/components/X402Fetch.tsx @@ -0,0 +1,587 @@ +'use client'; + +import { useState } from 'react'; +import { useWallet } from '@/contexts/WalletContext'; +import { useHathor } from '@/contexts/HathorContext'; +import { config } from '@/lib/config'; +import { HathorRPCService } from '@/lib/hathorRPC'; +import { toast } from '@/lib/toast'; +import { formatAddress } from '@/lib/utils'; +import { addPayment, getPayments, PaymentRecord } from '@/lib/paymentHistory'; + +// --- types ----------------------------------------------------------------- + +interface PaymentOption { + scheme: 'hathor-direct' | 'hathor-direct-upto' | string; + network: string; + asset: string; + amount: string; + description?: string; + resource: string; + payTo: string; + maxTimeoutSeconds?: number; + extra: { + requestId: string; + facilitatorUrl?: string; + }; +} + +type Step = + | 'idle' + | 'fetching' + | 'got402' + | 'confirming_send' + | 'waiting_mempool' + | 'signing' + | 'retrying' + | 'done' + | 'error'; + +// --- helpers --------------------------------------------------------------- + +async function pollMempool(nodeUrl: string, txId: string, maxAttempts = 30): Promise { + for (let i = 0; i < maxAttempts; i++) { + try { + const resp = await fetch(`${nodeUrl}/transaction?id=${encodeURIComponent(txId)}`); + const data = await resp.json(); + if (data?.success) return data; + } catch { + /* retry */ + } + await new Promise((r) => setTimeout(r, 1000)); + } + throw new Error('Transaction not visible at fullnode after 30s'); +} + +function getInputAddresses(tx: any): Set { + const addrs = new Set(); + for (const inp of tx?.inputs || []) { + const a = inp?.decoded?.address; + if (a) addrs.add(a); + } + return addrs; +} + +function describeAmount(atomicStr: string) { + const n = parseInt(atomicStr, 10); + if (!Number.isFinite(n)) return atomicStr; + return `${(n / 100).toFixed(2)} HTR`; +} + +function describeScheme(scheme: string) { + if (scheme === 'hathor-direct-upto') return 'upto'; + if (scheme === 'hathor-direct') return 'direct'; + return scheme; +} + +// --- component ------------------------------------------------------------- + +export function X402Fetch() { + const { sendTransaction, signWithAddress, getUtxos, address } = useWallet(); + const { network, isConnected } = useHathor(); + + const [url, setUrl] = useState(config.defaultDemoUrl); + const [step, setStep] = useState('idle'); + const [accept, setAccept] = useState(null); + const [resourceData, setResourceData] = useState(null); + const [paymentInfo, setPaymentInfo] = useState(null); + const [error, setError] = useState(null); + const [txId, setTxId] = useState(null); + const [history, setHistory] = useState(() => getPayments()); + + const reset = () => { + setStep('idle'); + setAccept(null); + setResourceData(null); + setPaymentInfo(null); + setError(null); + setTxId(null); + }; + + // Step 1: GET → expect 402 + const handleFetch = async () => { + reset(); + setStep('fetching'); + try { + const r = await fetch(url, { mode: 'cors' }); + if (r.ok) { + setResourceData(await r.json()); + setStep('done'); + toast.success('Resource is free!'); + return; + } + if (r.status !== 402) throw new Error(`Unexpected status ${r.status}`); + const body = await r.json(); + if (!body?.accepts?.length) throw new Error('No accepts in 402 body'); + setAccept(body.accepts[0]); + setStep('got402'); + } catch (err: any) { + setError(err.message); + setStep('error'); + } + }; + + // Step 2: send tx, sign requestId, retry + const handlePay = async () => { + if (!accept || !isConnected || !address) return; + const opt = accept; + + try { + // We assume the wallet shared address-0 (its first address) at connect + // time — true for WalletConnect/Reown's session approval flow (see + // hathor-wallet/src/sagas/reown.js:184). To make the on-chain tx's + // input(s) provably belong to address-0, we explicitly fetch UTXOs + // filtered to that address and hand them to htr_sendTransaction as + // `inputs`. The wallet then *must* use only those UTXOs, so every + // input address in the resulting tx is address-0, and signing with + // addressIndex: 0 produces a signature the verifier accepts. + const PAYER_INDEX = 0; + // Address-0 is what WalletConnect shares at session approval + // (hathor-wallet/src/sagas/reown.js:184 derives `getAddressAtIndex(0)`), + // so the connected address is address-0 by definition. We use it both + // as the FROM (filter UTXOs to spend) and as the change recipient so + // address-0 keeps being funded for subsequent payments. + if (!address) throw new Error('Wallet not connected'); + const payerAddress = address; + const amount = parseInt(String(opt.amount).replace(/[^0-9]/g, ''), 10); + + // Step 1: list the connected address's UTXOs (asset-filtered). + setStep('confirming_send'); + toast.info('Listing your UTXOs at address-0...'); + const utxoDetails = await getUtxos({ + network, + filterAddress: payerAddress, + token: opt.asset || '00', + onlyAvailableUtxos: true, + maxUtxos: 255, + }); + const available = (utxoDetails?.utxos || []) + .filter((u) => !u.locked) + .map((u) => ({ ...u, _amt: Number(u.amount) })) + .sort((a, b) => b._amt - a._amt); // largest first → fewer inputs + + // Greedy-pick enough UTXOs to cover the payment. + const chosen: typeof available = []; + let total = 0; + for (const u of available) { + chosen.push(u); + total += u._amt; + if (total >= amount) break; + } + if (total < amount) { + throw new Error( + `Address-0 (${payerAddress.slice(0, 8)}...) has ${(total / 100).toFixed(2)} HTR available — need ${(amount / 100).toFixed(2)} HTR. Move funds to this address and retry.` + ); + } + + toast.info('Confirm the payment in your wallet...'); + const sendOutput: any = { + address: opt.payTo, + // The wallet RPC validates `value` as a digit string (not a number). + value: String(amount), + }; + if (opt.asset && opt.asset !== '00') sendOutput.token = opt.asset; + + const sendResp = await sendTransaction({ + network, + outputs: [sendOutput], + inputs: chosen.map((u) => ({ txId: u.tx_id, index: u.index })), + // ALWAYS route change to address-0 so it stays funded for future + // payments. Otherwise the wallet defaults change to its + // `first_empty` address and address-0 slowly drains. + changeAddress: payerAddress, + push_tx: true, + }); + const sentTxId = HathorRPCService.extractTxId(sendResp); + if (!sentTxId) throw new Error('Wallet did not return a transaction hash'); + setTxId(sentTxId); + toast.success(`Payment broadcast — ${sentTxId.slice(0, 10)}...`); + + // Wait until our fullnode sees the tx — no need for first_block. Capture + // the response so we can validate against the actual on-chain inputs. + setStep('waiting_mempool'); + const mempoolResp = await pollMempool(config.hathorNodeUrls[network], sentTxId); + const inputAddrs = getInputAddresses(mempoolResp?.tx); + + // Sign the server-issued requestId with address-0's key. + setStep('signing'); + toast.info('Confirm the signature in your wallet...'); + const signed = await signWithAddress({ + network, + message: opt.extra.requestId, + addressIndex: PAYER_INDEX, + }); + + // Sanity check — with explicit UTXO selection all inputs *should* be + // from address-0. If something else slipped in (wallet implementation + // quirk), surface a precise error matching what the verifier will say. + if (!inputAddrs.has(signed.address.address)) { + throw new Error( + `Signed address (${signed.address.address.slice(0, 8)}...) is not among this tx's input addresses ` + + `[${[...inputAddrs].map((a) => a.slice(0, 8) + '...').join(', ')}]. ` + + `Unexpected — the wallet didn't honour the explicit input list.` + ); + } + + // Retry the original GET with the proof header. The verifier expects a + // multi-signature payload (one {address, signature} entry per unique + // input address). The dApp constrains inputs to address-0, so the array + // is always length 1. + setStep('retrying'); + const payload = { + x402Version: 2, + scheme: opt.scheme, + network: opt.network, + payload: { + txId: sentTxId, + signatures: [ + { address: signed.address.address, signature: signed.signature }, + ], + requestId: opt.extra.requestId, + }, + }; + const headerB64 = + typeof window !== 'undefined' && typeof window.btoa === 'function' + ? window.btoa(JSON.stringify(payload)) + : Buffer.from(JSON.stringify(payload)).toString('base64'); + + const paidResp = await fetch(url, { + mode: 'cors', + headers: { 'PAYMENT-SIGNATURE': headerB64 }, + }); + if (!paidResp.ok) { + const errBody = await paidResp.json().catch(() => ({})); + throw new Error(errBody.reason || errBody.error || `Rejected: ${paidResp.status}`); + } + const data = await paidResp.json(); + setResourceData(data?.data ?? data); + setPaymentInfo(data?.payment ?? null); + + // Record locally for the history panel + /payment/[txId] page. + const record: PaymentRecord = { + txId: sentTxId, + url, + route: opt.resource, + scheme: (opt.scheme as PaymentRecord['scheme']) || 'hathor-direct', + amount: opt.amount, + chargedAmount: data?.payment?.chargedAmount, + refundAmount: data?.payment?.refundAmount, + refundTxId: data?.payment?.refundTxId, + asset: opt.asset, + payTo: opt.payTo, + payerAddress: signed.address.address, + network: opt.network, + status: 'served', + ts: Date.now(), + }; + addPayment(record); + setHistory(getPayments()); + + setStep('done'); + toast.success('Resource received!'); + // Intentionally NOT calling refreshBalance here — htr_getBalance + // prompts the wallet on some adapters, which is noisy right after the + // user just signed twice. The user can hit "Load Balance" on the + // BalanceCard explicitly if they want a fresh number. + } catch (err: any) { + setError(err.message); + setStep('error'); + toast.error(err.message); + } + }; + + const isUpto = accept?.scheme === 'hathor-direct-upto'; + const amountLabel = accept ? describeAmount(accept.amount) : ''; + + return ( +
+ {/* URL input + presets */} +
+

Access a paid resource

+

+ Enter an x402-enabled URL. Click Fetch to load it. + On 402, your wallet will sign a payment. +

+ +
+ setUrl(e.target.value)} + className="flex-1 bg-slate-700 border border-slate-600 rounded-lg px-4 py-3 text-white font-mono text-sm" + disabled={step !== 'idle' && step !== 'error' && step !== 'done'} + /> + +
+ +
+ Try: + + +
+
+ + {/* Fetching */} + {step === 'fetching' && ( +
+
Fetching resource...
+
+ )} + + {/* Got 402 */} + {step === 'got402' && accept && ( +
+
+ 💰 +
+

402 — Payment Required

+

+ Scheme: {describeScheme(accept.scheme)} +

+
+
+ +
+
+ {isUpto ? 'Max authorized' : 'Pay'} + + {isUpto ? `up to ${amountLabel}` : amountLabel} + +
+
+ Seller + {formatAddress(accept.payTo)} +
+
+ Network + {accept.network} +
+ {accept.description && ( +
+ Description + {accept.description} +
+ )} + {isUpto && ( +
+ After + + Server charges actual usage, remainder refunded + +
+ )} +
+ +
+ + +
+
+ )} + + {/* In-flight steps */} + {step === 'confirming_send' && ( +
+
Awaiting wallet confirmation...
+

Approve the payment in your wallet

+
+ )} + + {step === 'waiting_mempool' && ( +
+
Waiting for the fullnode to see the tx...
+ {txId &&

{txId.slice(0, 24)}...

} +

~1–2 seconds

+
+ )} + + {step === 'signing' && ( +
+
Awaiting signature...
+

Approve the message signature in your wallet

+
+ )} + + {step === 'retrying' && ( +
+
Submitting payment proof...
+
+ )} + + {/* Done */} + {step === 'done' && resourceData && ( +
+
+ +
+

Resource received

+ {txId && ( +

+ via tx{' '} + + {formatAddress(txId)} + +

+ )} +
+
+ + {paymentInfo?.scheme === 'hathor-direct-upto' && + paymentInfo?.chargedAmount != null && ( +
+
📊 Upto settlement
+
+
+ Max authorized + {describeAmount(paymentInfo.amount)} +
+
+ Charged + + {describeAmount(paymentInfo.chargedAmount)} + +
+ {paymentInfo.refundAmount && parseInt(paymentInfo.refundAmount, 10) > 0 && ( +
+ Refunded + + {describeAmount(paymentInfo.refundAmount)} + +
+ )} + {paymentInfo.refundTxId && ( +
+ Refund tx + + {formatAddress(paymentInfo.refundTxId)} + +
+ )} + {paymentInfo.refundError && ( +
+ Refund error + {paymentInfo.refundError} +
+ )} +
+
+ )} + +
+            {JSON.stringify(resourceData, null, 2)}
+          
+ +
+ )} + + {/* Error */} + {step === 'error' && ( +
+

Error

+

{error}

+ +
+ )} + + {/* History */} + {history.length > 0 && ( +
+

Payment history

+
+ {history.map((e) => { + const badgeClass = + e.scheme === 'hathor-direct-upto' + ? 'bg-blue-500/20 text-blue-400' + : 'bg-amber-500/20 text-amber-400'; + const badgeLabel = describeScheme(e.scheme); + const ts = new Date(e.ts); + return ( + +
+

{e.url}

+

+ {ts.toLocaleTimeString()} — {formatAddress(e.txId)} + {e.chargedAmount != null && ( + + charged {describeAmount(e.chargedAmount)} + {e.refundAmount && parseInt(e.refundAmount, 10) > 0 && + ` · refunded ${describeAmount(e.refundAmount)}`} + + )} +

+
+ {badgeLabel} +
+ ); + })} +
+
+ )} +
+ ); +} diff --git a/dapp/components/ui/button.tsx b/dapp/components/ui/button.tsx new file mode 100644 index 0000000..801d09b --- /dev/null +++ b/dapp/components/ui/button.tsx @@ -0,0 +1,44 @@ +import * as React from "react" +import { cn } from "@/lib/utils" + +export interface ButtonProps + extends React.ButtonHTMLAttributes { + variant?: 'default' | 'outline' | 'ghost' | 'destructive' + size?: 'default' | 'sm' | 'lg' | 'icon' +} + +const Button = React.forwardRef( + ({ className, variant = 'default', size = 'default', ...props }, ref) => { + const baseStyles = "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none" + + const variants = { + default: "bg-blue-600 text-white hover:bg-blue-700", + outline: "border border-slate-700 bg-transparent hover:bg-slate-700 text-white", + ghost: "hover:bg-slate-700 text-white", + destructive: "bg-red-600 text-white hover:bg-red-700" + } + + const sizes = { + default: "h-10 py-2 px-4", + sm: "h-9 px-3 rounded-md", + lg: "h-11 px-8 rounded-md", + icon: "h-10 w-10" + } + + return ( + + ) +}) +SelectTrigger.displayName = "SelectTrigger" + +const SelectValue = ({ placeholder, labels }: { placeholder?: string; labels?: Record }) => { + const context = React.useContext(SelectContext) + if (!context) throw new Error('SelectValue must be used within Select') + + const displayValue = labels?.[context.value] || context.value || placeholder + return {displayValue} +} + +const SelectContent = ({ children, className }: { children: React.ReactNode; className?: string }) => { + const context = React.useContext(SelectContext) + if (!context) throw new Error('SelectContent must be used within Select') + + if (!context.open) return null + + return ( + <> +
context.setOpen(false)} + /> +
+ {children} +
+ + ) +} + +interface SelectItemProps { + value: string + disabled?: boolean + className?: string + children: React.ReactNode +} + +const SelectItem = ({ value, disabled, className, children }: SelectItemProps) => { + const context = React.useContext(SelectContext) + if (!context) throw new Error('SelectItem must be used within Select') + + const handleClick = () => { + if (!disabled) { + context.onValueChange(value) + context.setOpen(false) + } + } + + return ( +
+ {children} +
+ ) +} + +export { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } diff --git a/dapp/contexts/HathorContext.tsx b/dapp/contexts/HathorContext.tsx new file mode 100644 index 0000000..de7f483 --- /dev/null +++ b/dapp/contexts/HathorContext.tsx @@ -0,0 +1,111 @@ +'use client'; + +import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'; +import { HathorCoreAPI } from '@/lib/hathorCoreAPI'; +import { config, Network } from '@/lib/config'; +import { useWalletConnect } from './WalletConnectContext'; +import { useMetaMask } from './MetaMaskContext'; +import { useWallet } from './WalletContext'; + +interface HathorContextType { + isConnected: boolean; + address: string | null; + network: Network; + coreAPI: HathorCoreAPI; + connectWallet: () => Promise; + disconnectWallet: () => Promise; + switchNetwork: (network: Network) => Promise; +} + +const HathorContext = createContext(undefined); + +const NETWORK_STORAGE_KEY = 'hathor_selected_network'; + +const getInitialNetwork = (): Network => { + if (typeof window !== 'undefined') { + const stored = localStorage.getItem(NETWORK_STORAGE_KEY); + if (stored === 'mainnet' || stored === 'testnet') return stored; + } + return config.defaultNetwork; +}; + +export function HathorProvider({ children }: { children: ReactNode }) { + const walletConnect = useWalletConnect(); + const metaMask = useMetaMask(); + const wallet = useWallet(); + const [address, setAddress] = useState(null); + const [network, setNetwork] = useState(getInitialNetwork); + const [coreAPI, setCoreAPI] = useState(() => new HathorCoreAPI(getInitialNetwork())); + + const isConnected = walletConnect.isConnected || metaMask.isConnected; + + useEffect(() => { + setCoreAPI(new HathorCoreAPI(network)); + }, [network]); + + useEffect(() => { + if (isConnected) { + if (walletConnect.isConnected) { + const addr = walletConnect.getFirstAddress(); + setAddress(addr); + const walletNetwork = walletConnect.getConnectedNetwork(); + if (walletNetwork && walletNetwork !== network) { + setNetwork(walletNetwork); + localStorage.setItem(NETWORK_STORAGE_KEY, walletNetwork); + } + } else if (metaMask.isConnected) { + setAddress(metaMask.address); + if (metaMask.walletNetwork && metaMask.walletNetwork !== network) { + setNetwork(metaMask.walletNetwork); + localStorage.setItem(NETWORK_STORAGE_KEY, metaMask.walletNetwork); + } + } + } else { + setAddress(null); + wallet.setBalance(0n); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isConnected, walletConnect.isConnected, metaMask.isConnected]); + + const connectWallet = async () => { + await walletConnect.connect(network); + }; + + const disconnectWallet = async () => { + if (walletConnect.isConnected) await walletConnect.disconnect(); + if (metaMask.isConnected) await metaMask.disconnect(); + localStorage.removeItem('wallet_type'); + }; + + const switchNetwork = async (newNetwork: Network) => { + setNetwork(newNetwork); + localStorage.setItem(NETWORK_STORAGE_KEY, newNetwork); + if (typeof (walletConnect as any).switchNetwork === 'function') { + (walletConnect as any).switchNetwork(newNetwork).catch(() => {}); + } + }; + + return ( + + {children} + + ); +} + +export function useHathor() { + const context = useContext(HathorContext); + if (context === undefined) { + throw new Error('useHathor must be used within a HathorProvider'); + } + return context; +} diff --git a/dapp/contexts/MetaMaskContext.tsx b/dapp/contexts/MetaMaskContext.tsx new file mode 100644 index 0000000..b2d3e5a --- /dev/null +++ b/dapp/contexts/MetaMaskContext.tsx @@ -0,0 +1,212 @@ +'use client'; + +import React, { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import { HathorRPCService } from '@/lib/hathorRPC'; +import { config, Network } from '@/lib/config'; + +interface IMetaMaskContext { + address: string | null; + walletNetwork: Network | null; + isConnected: boolean; + isInstalled: boolean; + connect: (targetNetwork?: Network) => Promise; + disconnect: () => Promise; + request: (method: string, params?: any) => Promise; +} + +const MetaMaskContext = createContext({} as IMetaMaskContext); + +const SNAP_ID = 'npm:@hathor/snap'; +const SNAP_VERSION = '*'; // Use latest version + +export function MetaMaskProvider({ children }: { children: ReactNode | ReactNode[] }) { + const [address, setAddress] = useState(null); + const [walletNetwork, setWalletNetwork] = useState(null); + const [isConnected, setIsConnected] = useState(false); + const [isInstalled, setIsInstalled] = useState(false); + + // Helper method to call MetaMask Snap and parse JSON response + const metamask_request = useCallback(async (method: string, params?: any): Promise => { + if (!window.ethereum) { + throw new Error('MetaMask is not installed'); + } + + // Build request object - only include params if defined (MetaMask requires params to be object/array, not undefined) + const request: { method: string; params?: any } = { method }; + if (params !== undefined) { + request.params = params; + } + + const resultStr = await window.ethereum.request({ + method: 'wallet_invokeSnap', + params: { + snapId: SNAP_ID, + request, + }, + }); + + return typeof resultStr === 'string' ? JSON.parse(resultStr) : resultStr; + }, []); + + // Create RPC service instance configured for MetaMask + const [rpcService] = useState(() => new HathorRPCService(config.useMockWallet, undefined, undefined, metamask_request)); + + // Check if MetaMask is installed + useEffect(() => { + const checkMetaMask = () => { + if (typeof window !== 'undefined' && window.ethereum) { + setIsInstalled(true); + } + }; + checkMetaMask(); + }, []); + + // Check persisted connection on mount + useEffect(() => { + const checkPersistedConnection = async () => { + if (typeof window === 'undefined' || !window.ethereum) return; + + try { + const walletType = localStorage.getItem('wallet_type'); + if (walletType !== 'metamask') return; + + // Check if snap is already connected + const snaps = await window.ethereum.request({ + method: 'wallet_getSnaps', + }); + + if (snaps?.[SNAP_ID]) { + // Get wallet information from snap (no user approval required) + const result = await rpcService.getWalletInformation(); + + // MetaMask Snap returns response in nested format + const walletInfo = (result as any)?.response || result; + if (walletInfo?.address0) { + setAddress(walletInfo.address0); + setIsConnected(true); + // Store the wallet's network + if (walletInfo.network) { + setWalletNetwork(walletInfo.network as Network); + } + } + } + } catch (error) { + console.error('Failed to check persisted MetaMask connection:', error); + } + }; + + checkPersistedConnection(); + }, [rpcService]); + + const connect = useCallback(async (targetNetwork?: Network) => { + if (!window.ethereum) { + throw new Error('MetaMask is not installed. Please install MetaMask extension.'); + } + + // Use provided network or default from config + const dappNetwork = targetNetwork || config.defaultNetwork; + + try { + // Request snap connection + const result = await window.ethereum.request({ + method: 'wallet_requestSnaps', + params: { + [SNAP_ID]: { + version: SNAP_VERSION, + }, + }, + }); + + if (!result?.[SNAP_ID]) { + throw new Error('Failed to connect to Hathor Snap'); + } + + // Get wallet information from snap (includes network) + const walletResult = await rpcService.getWalletInformation(); + + // MetaMask Snap returns response in nested format + const walletInfo = (walletResult as any)?.response || walletResult; + if (!walletInfo?.address0) { + throw new Error('Failed to get wallet information from snap'); + } + + // Check if wallet's network matches dapp's network + const walletNetwork = walletInfo.network; + let finalAddress = walletInfo.address0; + + if (walletNetwork !== dappNetwork) { + console.log(`Wallet is on ${walletNetwork}, switching to ${dappNetwork}...`); + try { + await metamask_request('htr_changeNetwork', { network: walletNetwork, newNetwork: dappNetwork }); + // After network change, get updated wallet info with new address + const updatedWalletResult = await rpcService.getWalletInformation(); + const updatedWalletInfo = (updatedWalletResult as any)?.response || updatedWalletResult; + if (updatedWalletInfo?.address0) { + finalAddress = updatedWalletInfo.address0; + console.log(`Network switched, new address: ${finalAddress}`); + } + } catch (error) { + console.warn('Failed to change network:', error); + // Continue anyway - user can manually switch later + } + } + + setAddress(finalAddress); + setIsConnected(true); + setWalletNetwork(dappNetwork); + localStorage.setItem('wallet_type', 'metamask'); + localStorage.setItem('address', finalAddress); + } catch (error: any) { + console.error('Failed to connect to MetaMask Snap:', error); + throw new Error(error?.message || 'Failed to connect to MetaMask Snap'); + } + }, [rpcService, metamask_request]); + + const disconnect = useCallback(async () => { + setAddress(null); + setWalletNetwork(null); + setIsConnected(false); + localStorage.removeItem('wallet_type'); + localStorage.removeItem('address'); + }, []); + + const request = useCallback( + async (method: string, params?: any): Promise => { + if (!isConnected) { + throw new Error('MetaMask Snap is not connected'); + } + + try { + const result = await metamask_request(method, params); + return result as T; + } catch (error: any) { + console.error('MetaMask Snap request failed:', error); + throw new Error(error?.message || 'MetaMask Snap request failed'); + } + }, + [isConnected, metamask_request] + ); + + const value = useMemo( + () => ({ + address, + walletNetwork, + isConnected, + isInstalled, + connect, + disconnect, + request, + }), + [address, walletNetwork, isConnected, isInstalled, connect, disconnect, request] + ); + + return {children}; +} + +export function useMetaMask() { + const context = useContext(MetaMaskContext); + if (context === undefined) { + throw new Error('useMetaMask must be used within a MetaMaskProvider'); + } + return context; +} diff --git a/dapp/contexts/UnifiedWalletContext.tsx b/dapp/contexts/UnifiedWalletContext.tsx new file mode 100644 index 0000000..4c882f3 --- /dev/null +++ b/dapp/contexts/UnifiedWalletContext.tsx @@ -0,0 +1,96 @@ +'use client'; + +import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react'; +import { WalletAdapter, WalletType } from '@/types/wallet'; +import { useMetaMask } from './MetaMaskContext'; +import { useWalletConnect } from './WalletConnectContext'; + +interface UnifiedWalletContextType { + adapter: WalletAdapter | null; + walletType: WalletType; + setWalletType: (type: WalletType) => void; +} + +const UnifiedWalletContext = createContext(undefined); + +export function UnifiedWalletProvider({ children }: { children: ReactNode }) { + const metaMask = useMetaMask(); + const walletConnect = useWalletConnect(); + const [walletType, setWalletTypeState] = useState(null); + + // Load wallet type from localStorage on mount + useEffect(() => { + const storedWalletType = localStorage.getItem('wallet_type') as WalletType; + if (storedWalletType) { + setWalletTypeState(storedWalletType); + } + }, []); + + // Update wallet type when connections change + useEffect(() => { + if (metaMask.isConnected && walletType !== 'metamask') { + setWalletTypeState('metamask'); + } else if (walletConnect.isConnected && walletType !== 'walletconnect') { + setWalletTypeState('walletconnect'); + } else if (!metaMask.isConnected && !walletConnect.isConnected && walletType !== null) { + setWalletTypeState(null); + } + }, [metaMask.isConnected, walletConnect.isConnected, walletType]); + + // Determine which adapter to use based on wallet type + const adapter: WalletAdapter | null = (() => { + if (walletType === 'metamask' && metaMask.isConnected) { + return { + isConnected: metaMask.isConnected, + isInitializing: false, + address: metaMask.address, + connect: metaMask.connect, + disconnect: metaMask.disconnect, + request: metaMask.request, + }; + } else if (walletType === 'walletconnect' && walletConnect.isConnected) { + const connectedNetwork = walletConnect.getConnectedNetwork() || 'privatenet'; + return { + isConnected: walletConnect.isConnected, + isInitializing: walletConnect.isInitializing, + address: walletConnect.getFirstAddress(), + connect: () => walletConnect.connect(undefined), + disconnect: walletConnect.disconnect, + request: async (method: string, params?: any): Promise => { + if (!walletConnect.client || !walletConnect.session) { + throw new Error('WalletConnect not connected'); + } + return walletConnect.client.request({ + chainId: `hathor:${connectedNetwork}`, + topic: walletConnect.session.topic, + request: { method, params }, + }); + }, + }; + } + return null; + })(); + + const setWalletType = (type: WalletType) => { + setWalletTypeState(type); + if (type) { + localStorage.setItem('wallet_type', type); + } else { + localStorage.removeItem('wallet_type'); + } + }; + + return ( + + {children} + + ); +} + +export function useUnifiedWallet() { + const context = useContext(UnifiedWalletContext); + if (context === undefined) { + throw new Error('useUnifiedWallet must be used within a UnifiedWalletProvider'); + } + return context; +} diff --git a/dapp/contexts/WalletConnectContext.tsx b/dapp/contexts/WalletConnectContext.tsx new file mode 100644 index 0000000..0b2be3b --- /dev/null +++ b/dapp/contexts/WalletConnectContext.tsx @@ -0,0 +1,300 @@ +'use client'; + +import React, { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState, useRef } from 'react'; +import Client from '@walletconnect/sign-client'; +import { PairingTypes, SessionTypes } from '@walletconnect/types'; +import { Web3Modal } from '@web3modal/standalone'; +import { getSdkError } from '@walletconnect/utils'; +import { WALLETCONNECT_PROJECT_ID } from '@/lib/walletConnectConfig'; +import { initializeWalletConnectClient } from '@/lib/walletConnectClient'; +import { Network } from '@/lib/config'; + +interface IWalletConnectContext { + client: Client | undefined; + session: SessionTypes.Struct | undefined; + connect: (targetNetwork?: Network, pairing?: { topic: string }) => Promise; + disconnect: () => Promise; + chains: string[]; + pairings: PairingTypes.Struct[]; + accounts: string[]; + setChains: React.Dispatch>; + getFirstAddress: () => string; + getConnectedNetwork: () => Network | null; + isConnected: boolean; + isInitializing: boolean; +} + +const WalletConnectContext = createContext({} as IWalletConnectContext); + +const web3Modal = new Web3Modal({ + projectId: WALLETCONNECT_PROJECT_ID, + themeMode: 'dark', + walletConnectVersion: 2, +}); + +export function WalletConnectProvider({ children }: { children: ReactNode | ReactNode[] }) { + const [client, setClient] = useState(); + const [pairings, setPairings] = useState([]); + const [session, setSession] = useState(); + const [accounts, setAccounts] = useState([]); + const [chains, setChains] = useState([]); + const [clientInitialized, setClientInitialized] = useState(false); + const [isInitializing, setIsInitializing] = useState(false); + const initializationAttempted = useRef(false); + + const reset = () => { + setSession(undefined); + setAccounts([]); + setChains([]); + localStorage.removeItem('wallet_type'); + localStorage.removeItem('address'); + }; + + const onSessionConnected = useCallback(async (_session: SessionTypes.Struct) => { + const allNamespaceAccounts = Object.values(_session.namespaces) + .map((namespace) => namespace.accounts) + .flat(); + const allNamespaceChains = Object.keys(_session.namespaces); + + setSession(_session); + setChains(allNamespaceChains); + setAccounts(allNamespaceAccounts); + + // Store wallet type and address in localStorage + localStorage.setItem('wallet_type', 'walletconnect'); + if (_session.namespaces.hathor?.accounts?.[0]) { + const [, , address] = _session.namespaces.hathor.accounts[0].split(':'); + localStorage.setItem('address', address); + } + }, []); + + const getFirstAddress = useCallback(() => { + if (!session?.namespaces?.hathor?.accounts?.[0]) { + return ''; + } + const [, , addr] = session.namespaces.hathor.accounts[0].split(':'); + return addr; + }, [session]); + + const getConnectedNetwork = useCallback((): Network | null => { + if (!session?.namespaces?.hathor?.accounts?.[0]) { + return null; + } + const [, network] = session.namespaces.hathor.accounts[0].split(':'); + return network as Network; + }, [session]); + + const subscribeToEvents = useCallback( + async (_client: Client) => { + if (!_client) { + throw new Error('WalletConnect is not initialized'); + } + + _client.on('session_ping', (args) => { + console.log('EVENT', 'session_ping', args); + }); + + _client.on('session_event', (args) => { + console.log('EVENT', 'session_event', args); + }); + + _client.on('session_update', ({ topic, params }) => { + console.log('EVENT', 'session_update', { topic, params }); + const { namespaces } = params; + const _session = _client.session.get(topic); + const updatedSession = { ..._session, namespaces }; + onSessionConnected(updatedSession); + }); + + _client.on('session_delete', () => { + console.log('EVENT', 'session_delete'); + reset(); + }); + }, + [onSessionConnected] + ); + + const checkPersistedState = useCallback( + async (_client: Client) => { + if (!_client) { + throw new Error('WalletConnect is not initialized'); + } + setPairings(_client.pairing.getAll({ active: true })); + + if (session) return; + if (_client.session.length) { + const lastKeyIndex = _client.session.keys.length - 1; + const _session = _client.session.get(_client.session.keys[lastKeyIndex]); + await onSessionConnected(_session); + return _session; + } + }, + [session, onSessionConnected] + ); + + useEffect(() => { + if (client && !clientInitialized) { + (async () => { + try { + await subscribeToEvents(client); + await checkPersistedState(client); + setClientInitialized(true); + } catch (error) { + console.error('Failed to setup WalletConnect:', error); + } + })(); + } + }, [client, clientInitialized, subscribeToEvents, checkPersistedState]); + + useEffect(() => { + if (initializationAttempted.current || isInitializing || client) return; + + initializationAttempted.current = true; + setIsInitializing(true); + (async () => { + try { + const _client = await initializeWalletConnectClient(); + setClient(_client); + } catch (error) { + console.error('Failed to initialize WalletConnect on mount:', error); + initializationAttempted.current = false; + } finally { + setIsInitializing(false); + } + })(); + }, []); + + const connect = useCallback( + async (targetNetwork?: Network, pairing?: { topic: string }) => { + if (!client) { + throw new Error('WalletConnect client is not initialized yet. Please wait a moment and try again.'); + } + + if (session) { + return; + } + + // Default to testnet if no network specified + const dappNetwork = targetNetwork || 'privatenet'; + + let unsubscribe: (() => void) | undefined; + + try { + const requiredNamespaces = { + hathor: { + methods: [ + 'htr_getAddress', + 'htr_getBalance', + 'htr_getConnectedNetwork', + 'htr_getWalletInformation', + 'htr_getUtxos', + 'htr_signWithAddress', + 'htr_sendTransaction', + ], + chains: [`hathor:${dappNetwork}`], + events: [], + }, + }; + + const { uri, approval } = await client.connect({ + pairingTopic: pairing?.topic, + requiredNamespaces, + }); + + if (uri) { + const standaloneChains = Object.values(requiredNamespaces) + .map((namespace) => namespace.chains) + .flat() as string[]; + + web3Modal.openModal({ uri, standaloneChains }); + } + + const modalClosedPromise = new Promise((_, reject) => { + unsubscribe = web3Modal.subscribeModal((state) => { + if (!state.open) { + reject(new Error('User closed the modal')); + } + }); + }); + + const newSession = await Promise.race([approval(), modalClosedPromise]); + + // Extract network from the connected session + const connectedAccount = newSession.namespaces?.hathor?.accounts?.[0]; + if (connectedAccount) { + const [, walletNetwork] = connectedAccount.split(':'); + + // Validate network matches + if (walletNetwork !== dappNetwork) { + // Disconnect the session since networks don't match + await client.disconnect({ + topic: newSession.topic, + reason: getSdkError('USER_DISCONNECTED'), + }); + throw new Error(`Network mismatch: wallet is on ${walletNetwork} but dapp requires ${dappNetwork}. Please switch your wallet to ${dappNetwork} and try again.`); + } + } + + await onSessionConnected(newSession); + setPairings(client.pairing.getAll({ active: true })); + } catch (e) { + console.error('Failed to connect:', e); + throw e; + } finally { + if (unsubscribe) { + unsubscribe(); + } + web3Modal.closeModal(); + } + }, + [client, session, onSessionConnected] + ); + + const disconnect = useCallback(async () => { + if (!client) { + throw new Error('WalletConnect is not initialized'); + } + if (!session) { + throw new Error('Session is not connected'); + } + + try { + await client.disconnect({ + topic: session.topic, + reason: getSdkError('USER_DISCONNECTED'), + }); + } catch (error) { + console.error('SignClient.disconnect failed:', error); + } finally { + reset(); + } + }, [client, session]); + + const value = useMemo( + () => ({ + pairings, + accounts, + chains, + client, + session, + connect, + disconnect, + getFirstAddress, + getConnectedNetwork, + setChains, + isConnected: !!session, + isInitializing, + }), + [pairings, accounts, chains, client, session, connect, disconnect, getFirstAddress, getConnectedNetwork, setChains, isInitializing] + ); + + return {children}; +} + +export function useWalletConnect() { + const context = useContext(WalletConnectContext); + if (context === undefined) { + throw new Error('useWalletConnect must be used within a WalletConnectProvider'); + } + return context; +} diff --git a/dapp/contexts/WalletContext.tsx b/dapp/contexts/WalletContext.tsx new file mode 100644 index 0000000..c8db9e1 --- /dev/null +++ b/dapp/contexts/WalletContext.tsx @@ -0,0 +1,223 @@ +'use client'; + +import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'; +import { HathorRPCService } from '@/lib/hathorRPC'; +import { useUnifiedWallet } from './UnifiedWalletContext'; +import { config, Network } from '@/lib/config'; +import type { + GetAddressParams, + GetUtxosParams, + UtxoDetails, + SendTransactionParams, + SendTransactionResponse, + SignWithAddressParams, + SignWithAddressResponse, +} from '@/types/hathor'; + +// Read-only balance fallback via the fullnode — used when WalletConnect RPC +// rejects htr_getBalance (e.g. on certain wallets / mock mode). +async function fetchBalanceFromFullnode( + address: string, + tokenUid: string, + network: Network +): Promise { + const nodeUrl = config.hathorNodeUrls[network]; + const resp = await fetch(`${nodeUrl}/thin_wallet/address_balance?address=${address}`); + if (!resp.ok) throw new Error(`Fullnode balance query failed: ${resp.status}`); + const data = await resp.json(); + if (!data.success) throw new Error(`Fullnode balance query error: ${data.message}`); + const tokenBalance = data.tokens_data?.[tokenUid]; + if (!tokenBalance) return 0n; + return BigInt(tokenBalance.received - tokenBalance.spent); +} + +interface WalletContextType { + connected: boolean; + address: string | null; + balance: bigint; + walletBalance: number; + balanceVerified: boolean; + isLoadingBalance: boolean; + connectWallet: () => void; + disconnectWallet: () => void; + setBalance: React.Dispatch>; + refreshBalance: (tokenUid?: string, network?: Network) => Promise; + + // x402 hathor-direct primitives — the RPC methods we actually need. + getAddress: (params: GetAddressParams) => Promise<{ address: string; index: number; addressPath: string }>; + sendTransaction: (params: SendTransactionParams) => Promise; + signWithAddress: (params: SignWithAddressParams) => Promise; + getUtxos: (params: GetUtxosParams) => Promise; +} + +const WalletContext = createContext(undefined); + +const BALANCE_CACHE_KEY = 'hathor_balance_cache'; +const BALANCE_CACHE_DURATION = 15 * 60 * 1000; + +interface BalanceCache { + balance: string; + timestamp: number; + address: string; +} + +export function WalletProvider({ children }: { children: ReactNode }) { + const { adapter } = useUnifiedWallet(); + const [connected, setConnected] = useState(false); + const [address, setAddress] = useState(null); + const [balance, setBalance] = useState(0n); + const [balanceVerified, setBalanceVerified] = useState(false); + const [isLoadingBalance, setIsLoadingBalance] = useState(false); + const [rpcService] = useState(() => new HathorRPCService(config.useMockWallet)); + + const saveCachedBalance = (addr: string, bal: bigint) => { + try { + const cache: BalanceCache = { balance: bal.toString(), timestamp: Date.now(), address: addr }; + localStorage.setItem(BALANCE_CACHE_KEY, JSON.stringify(cache)); + } catch (error) { + console.error('Failed to save cached balance:', error); + } + }; + + const fetchBalance = async ( + addr: string, + _forceRefresh: boolean = false, + tokenUid: string = '00', + network: Network = 'testnet' + ) => { + if (!addr) return; + + if (config.useMockWallet) { + setBalance(100000n); + setBalanceVerified(true); + saveCachedBalance(addr, 100000n); + return; + } + + rpcService.setNetwork(network); + + setIsLoadingBalance(true); + setBalanceVerified(false); + try { + const balanceInfo = await rpcService.getBalance({ + network, + tokens: [tokenUid], + }); + + const responseData = (balanceInfo as any)?.response?.response || balanceInfo?.response; + const balanceData = responseData?.[0]?.balance?.unlocked; + + const balanceValue = + typeof balanceData === 'number' + ? BigInt(Math.floor(balanceData)) + : typeof balanceData === 'bigint' + ? balanceData + : 0n; + + setBalance(balanceValue); + setBalanceVerified(true); + saveCachedBalance(addr, balanceValue); + } catch (error: any) { + console.error('Balance fetch via wallet RPC failed; trying fullnode fallback:', error?.message); + try { + const balanceValue = await fetchBalanceFromFullnode(addr, tokenUid, network); + setBalance(balanceValue); + setBalanceVerified(true); + saveCachedBalance(addr, balanceValue); + } catch (fallbackError: any) { + console.error('Fullnode balance fallback also failed:', fallbackError?.message); + setBalance(0n); + setBalanceVerified(false); + } + } finally { + setIsLoadingBalance(false); + } + }; + + useEffect(() => { + if (adapter?.isConnected && adapter.address) { + rpcService.updateClientAndSession(undefined, undefined, adapter.request); + setAddress(adapter.address); + } else if (!adapter?.isConnected) { + setAddress(null); + setBalance(0n); + } + }, [adapter?.isConnected, adapter?.address, adapter?.request, rpcService]); + + const walletBalance = typeof balance === 'bigint' ? Number(balance) / 100 : 0; + + const connectWallet = () => { + setConnected(true); + }; + + const disconnectWallet = () => { + setConnected(false); + setAddress(null); + setBalance(0n); + setBalanceVerified(false); + setIsLoadingBalance(false); + }; + + const ensureConnected = () => { + if (!adapter?.isConnected) throw new Error('Wallet not connected'); + }; + + const getAddress: WalletContextType['getAddress'] = async (params) => { + ensureConnected(); + rpcService.setNetwork(params.network as Network); + return rpcService.getAddress(params); + }; + + const sendTransaction: WalletContextType['sendTransaction'] = async (params) => { + ensureConnected(); + rpcService.setNetwork(params.network as Network); + return rpcService.sendTransaction(params); + }; + + const signWithAddress: WalletContextType['signWithAddress'] = async (params) => { + ensureConnected(); + rpcService.setNetwork(params.network as Network); + return rpcService.signWithAddress(params); + }; + + const getUtxos: WalletContextType['getUtxos'] = async (params) => { + ensureConnected(); + rpcService.setNetwork(params.network as Network); + return rpcService.getUtxos(params); + }; + + const refreshBalance = async (tokenUid: string = '00', network: Network = 'testnet') => { + if (address) await fetchBalance(address, true, tokenUid, network); + }; + + return ( + + {children} + + ); +} + +export function useWallet() { + const context = useContext(WalletContext); + if (context === undefined) { + throw new Error('useWallet must be used within a WalletProvider'); + } + return context; +} diff --git a/dapp/e2e/homepage.spec.ts b/dapp/e2e/homepage.spec.ts new file mode 100644 index 0000000..f4fe8ce --- /dev/null +++ b/dapp/e2e/homepage.spec.ts @@ -0,0 +1,136 @@ +import { test, expect } from '@playwright/test' + +test.describe('Hathor dApp Homepage', () => { + test.beforeEach(async ({ page }) => { + // Navigate to homepage + await page.goto('/') + }) + + test('should load homepage successfully', async ({ page }) => { + // Check page title + await expect(page).toHaveTitle(/Hathor dApp/i) + + // Check main heading is visible + const heading = page.getByRole('heading', { level: 1 }) + await expect(heading).toBeVisible() + }) + + test('should display main template components', async ({ page }) => { + // Wait for page to be fully loaded + await page.waitForLoadState('networkidle') + + // Check that key UI elements are present + const pageContent = await page.textContent('body') + expect(pageContent).toBeTruthy() + + // The page should render without errors + const errors: Error[] = [] + page.on('pageerror', error => errors.push(error)) + await page.waitForTimeout(1000) + expect(errors).toHaveLength(0) + }) + + test('should display wallet connection button', async ({ page }) => { + // Look for wallet connect button + const connectButton = page.getByRole('button', { name: /connect/i }) + await expect(connectButton).toBeVisible() + }) + + test('should display getting started guide', async ({ page }) => { + // Check for getting started section + const gettingStarted = page.getByText('Getting Started') + await expect(gettingStarted).toBeVisible() + }) + + test('should display welcome message', async ({ page }) => { + // Check for welcome message + const welcomeText = page.getByText('Welcome to Your Hathor dApp') + await expect(welcomeText).toBeVisible() + }) + + test('should be responsive', async ({ page }) => { + // Test mobile viewport + await page.setViewportSize({ width: 375, height: 667 }) + await page.waitForLoadState('networkidle') + + const body = page.locator('body') + await expect(body).toBeVisible() + + // Test desktop viewport + await page.setViewportSize({ width: 1920, height: 1080 }) + await page.waitForLoadState('networkidle') + await expect(body).toBeVisible() + }) + + test('should not have console errors', async ({ page }) => { + const consoleErrors: string[] = [] + + page.on('console', msg => { + if (msg.type() === 'error') { + consoleErrors.push(msg.text()) + } + }) + + await page.goto('/') + await page.waitForLoadState('networkidle') + await page.waitForTimeout(2000) + + // Filter out expected errors (e.g., from mock wallet setup) + const unexpectedErrors = consoleErrors.filter(error => + !error.includes('WalletConnect') && + !error.includes('MetaMask') && + !error.includes('mock') + ) + + expect(unexpectedErrors).toHaveLength(0) + }) + + test('should have working navigation', async ({ page }) => { + await page.waitForLoadState('networkidle') + + // Check that the app is interactive + const body = page.locator('body') + const isInteractive = await body.isEnabled() + expect(isInteractive).toBe(true) + }) +}) + +test.describe('Mock Wallet Integration', () => { + test('should work with mock wallet enabled', async ({ page }) => { + // The app should be in mock mode based on environment variable + await page.goto('/') + await page.waitForLoadState('networkidle') + + // The page should render without crashing + const content = await page.textContent('body') + expect(content).toBeTruthy() + expect(content!.length).toBeGreaterThan(0) + }) + + test('should display balance section', async ({ page }) => { + await page.goto('/') + await page.waitForLoadState('networkidle') + + // Give time for any initial data loading + await page.waitForTimeout(2000) + + // Check if balance card is rendered + const balanceText = page.getByText('YOUR BALANCE') + await expect(balanceText).toBeVisible() + }) +}) + +test.describe('Resources Section', () => { + test('should display resource links', async ({ page }) => { + await page.goto('/') + await page.waitForLoadState('networkidle') + + // Check for resources section + const resources = page.getByText('Resources') + await expect(resources).toBeVisible() + + // Check for Hathor documentation link + const docsLink = page.getByRole('link', { name: /Hathor Documentation/i }) + await expect(docsLink).toBeVisible() + }) +}) diff --git a/dapp/lib/config.ts b/dapp/lib/config.ts new file mode 100644 index 0000000..563e669 --- /dev/null +++ b/dapp/lib/config.ts @@ -0,0 +1,26 @@ +export type Network = 'mainnet' | 'testnet' | 'privatenet'; + +export const config = { + useMockWallet: process.env.NEXT_PUBLIC_USE_MOCK_WALLET === 'true', + defaultNetwork: (process.env.NEXT_PUBLIC_DEFAULT_NETWORK || 'testnet') as Network, + hathorNodeUrls: { + privatenet: + process.env.NEXT_PUBLIC_HATHOR_NODE_URL_PRIVATENET || + 'https://node1.playground.testnet.hathor.network/v1a', + testnet: + process.env.NEXT_PUBLIC_HATHOR_NODE_URL_TESTNET || + 'https://node1.testnet.hathor.network/v1a', + mainnet: + process.env.NEXT_PUBLIC_HATHOR_NODE_URL_MAINNET || + 'https://node1.mainnet.hathor.network/v1a', + }, + + // x402 resource-server URL (where /weather, /generate, etc. live). + resourceServerUrl: + process.env.NEXT_PUBLIC_RESOURCE_SERVER_URL || 'http://localhost:3000', + + // Default URL the X402Fetch input box is seeded with. + get defaultDemoUrl() { + return `${this.resourceServerUrl}/weather`; + }, +}; diff --git a/dapp/lib/hathorCoreAPI.ts b/dapp/lib/hathorCoreAPI.ts new file mode 100644 index 0000000..90c2654 --- /dev/null +++ b/dapp/lib/hathorCoreAPI.ts @@ -0,0 +1,39 @@ +import { Network, config } from './config'; + +// Tiny read-only fullnode client. We only need what the hathor-direct flow +// needs: fetch a transaction (to render the payment detail page) and check a +// token balance. + +export class HathorCoreAPI { + private baseUrl: string; + + constructor(network: Network) { + this.baseUrl = config.hathorNodeUrls[network]; + } + + // GET /v1a/transaction?id= — returns { success, tx, meta: {voided_by, conflict_with, first_block, ...} } + async getTransaction(txId: string): Promise { + const response = await fetch(`${this.baseUrl}/transaction?id=${encodeURIComponent(txId)}`); + if (!response.ok) { + throw new Error(`Failed to fetch transaction: ${response.statusText}`); + } + return response.json(); + } + + async getTokenInfo(tokenUid: string): Promise<{ symbol: string; name: string } | null> { + if (tokenUid === '00' || /^0+$/.test(tokenUid)) { + return { symbol: 'HTR', name: 'Hathor' }; + } + try { + const response = await fetch(`${this.baseUrl}/thin_wallet/token?id=${encodeURIComponent(tokenUid)}`); + if (!response.ok) return null; + const data = await response.json(); + return { + symbol: data.symbol || tokenUid.slice(0, 8), + name: data.name || 'Unknown Token', + }; + } catch { + return null; + } + } +} diff --git a/dapp/lib/hathorRPC.ts b/dapp/lib/hathorRPC.ts new file mode 100644 index 0000000..d79d3d7 --- /dev/null +++ b/dapp/lib/hathorRPC.ts @@ -0,0 +1,252 @@ +import { + GetBalanceParams, + GetAddressParams, + GetUtxosParams, + UtxoDetails, + SendTransactionParams, + SendTransactionResponse, + SignWithAddressParams, + SignWithAddressResponse, +} from '@/types/hathor'; +import Client from '@walletconnect/sign-client'; +import { SessionTypes } from '@walletconnect/types'; +import { Network } from '@/lib/config'; + +type RequestFunction = (method: string, params?: any) => Promise; + +export class HathorRPCService { + private useMock: boolean; + private client: Client | undefined; + private session: SessionTypes.Struct | undefined; + private customRequest: RequestFunction | undefined; + private network: Network; + + constructor( + useMock: boolean = false, + client?: Client, + session?: SessionTypes.Struct, + customRequest?: RequestFunction, + network: Network = 'testnet' + ) { + this.useMock = useMock; + this.client = client; + this.session = session; + this.customRequest = customRequest; + this.network = network; + } + + updateClientAndSession( + client?: Client, + session?: SessionTypes.Struct, + customRequest?: RequestFunction, + network?: Network + ) { + this.client = client; + this.session = session; + this.customRequest = customRequest; + if (network) { + this.network = network; + } + } + + setNetwork(network: Network) { + this.network = network; + } + + async request(method: string, params?: any): Promise { + if (this.useMock) { + return this.mockRequest(method, params); + } + + if (this.customRequest) { + try { + const result = await this.customRequest(method, params); + // eslint-disable-next-line no-console + console.log(`RPC [${method}] response:`, result); + return result; + } catch (error: any) { + // eslint-disable-next-line no-console + console.error(`RPC [${method}] failed via custom request:`, error); + throw new Error(error?.message || 'RPC request failed'); + } + } + + if (!this.client || !this.session) { + throw new Error('Wallet not connected. Please connect your wallet.'); + } + + try { + // eslint-disable-next-line no-console + console.log( + `RPC [${method}] via WalletConnect (chain: hathor:${this.network}, topic: ${this.session.topic.slice(0, 8)}...)` + ); + const result = await this.client.request({ + chainId: `hathor:${this.network}`, + topic: this.session.topic, + request: { method, params }, + }); + // eslint-disable-next-line no-console + console.log(`RPC [${method}] response:`, result); + return result; + } catch (error: any) { + // eslint-disable-next-line no-console + console.error(`RPC [${method}] failed via WalletConnect:`, error); + throw new Error(error?.message || 'RPC request failed'); + } + } + + // --- typed wrappers -------------------------------------------------------- + + async getConnectedNetwork(): Promise<{ network: string; genesisHash: string }> { + return this.request('htr_getConnectedNetwork'); + } + + async getBalance(params: GetBalanceParams): Promise<{ response: any[] }> { + return this.request('htr_getBalance', params); + } + + // Every rpc-handler method returns `{type, response: {...actual data...}}` + // (see hathor-rpc-lib/packages/hathor-rpc-handler/src/rpcMethods/*.ts). + // Unwrap so callers get the inner shape directly. Snap responses sometimes + // double-wrap (response.response.*), so fall back to that too. + private unwrap(raw: any): T { + if (raw && typeof raw === 'object') { + if (raw.response && typeof raw.response === 'object') { + const inner = raw.response; + if (inner.response && typeof inner.response === 'object') return inner.response as T; + return inner as T; + } + } + return raw as T; + } + + async getAddress( + params: GetAddressParams + ): Promise<{ address: string; index: number; addressPath: string }> { + const raw = await this.request('htr_getAddress', params); + return this.unwrap(raw); + } + + async getWalletInformation(): Promise<{ address0: string; network: string }> { + const raw = await this.request('htr_getWalletInformation'); + return this.unwrap(raw); + } + + // Regular Hathor transaction send (no nano contracts). Implemented by the + // desktop wallet (hathor-wallet/src/sagas/reown.js:81), mobile wallet + // (hathor-wallet-mobile/src/sagas/reown.js:165), and the MetaMask snap + // (hathor-rpc-lib/packages/snap/src/index.tsx:81). + async sendTransaction(params: SendTransactionParams): Promise { + const raw = await this.request('htr_sendTransaction', params); + return this.unwrap(raw); + } + + // Sign an arbitrary message with the key of a specific wallet address. + // Returns a Bitcoin-compatible signed-message signature (bitcore.Message). + // We use this to bind a server-issued requestId to the payer. + async signWithAddress(params: SignWithAddressParams): Promise { + const raw = await this.request('htr_signWithAddress', params); + return this.unwrap(raw); + } + + // List the wallet's available UTXOs, optionally filtered to one address. + // We use this to pre-select UTXOs at address-0 so the resulting tx's inputs + // are guaranteed to come from there. + async getUtxos(params: GetUtxosParams): Promise { + const raw = await this.request('htr_getUtxos', params); + return this.unwrap(raw); + } + + // --- helpers --------------------------------------------------------------- + + // Extract a txId from the various shapes the RPC response can take. + static extractTxId(resp: SendTransactionResponse): string | undefined { + return ( + resp?.hash || + resp?.txId || + resp?.response?.hash || + resp?.response?.response?.hash + ); + } + + // --- mock --- (development only) ------------------------------------------- + + private async mockRequest(method: string, params?: any): Promise { + await new Promise((resolve) => setTimeout(resolve, 500)); + + switch (method) { + case 'htr_getConnectedNetwork': + return { network: 'testnet', genesisHash: '0x123...' } as T; + + case 'htr_getBalance': + return { + response: [ + { + token: { id: '00', name: 'Hathor', symbol: 'HTR' }, + balance: { unlocked: 1250.5, locked: 0 }, + tokenAuthorities: { + unlocked: { mint: false, melt: false }, + locked: { mint: false, melt: false }, + }, + transactions: 42, + lockExpires: null, + }, + ], + } as T; + + case 'htr_getAddress': { + const index = params?.index ?? 0; + return { + address: 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp', + index, + addressPath: `m/44'/280'/0'/0/${index}`, + } as T; + } + + case 'htr_getWalletInformation': + return { address0: 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp', network: 'testnet' } as T; + + case 'htr_sendTransaction': { + const fakeHash = + '00000000' + + Math.random().toString(36).substring(2, 15) + + Math.random().toString(36).substring(2, 15); + return { hash: fakeHash, success: true } as T; + } + + case 'htr_signWithAddress': { + const idx = params?.addressIndex ?? 0; + return { + message: params?.message ?? '', + signature: 'H' + Buffer.from(`mock-${idx}-${Date.now()}`).toString('base64'), + address: { + address: 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp', + index: idx, + addressPath: `m/44'/280'/0'/0/${idx}`, + }, + } as T; + } + + case 'htr_getUtxos': { + return { + total_amount_available: 100000, + total_utxos_available: 1, + total_amount_locked: 0, + total_utxos_locked: 0, + utxos: [ + { + address: 'WYBwT3xLpDnHNtYZiU52oanupVeDKhAvNp', + amount: 100000, + tx_id: '00mock' + Math.random().toString(36).substring(2, 15), + index: 0, + locked: false, + }, + ], + } as T; + } + + default: + throw new Error(`Mock not implemented for method: ${method}`); + } + } +} diff --git a/dapp/lib/paymentHistory.ts b/dapp/lib/paymentHistory.ts new file mode 100644 index 0000000..f23c008 --- /dev/null +++ b/dapp/lib/paymentHistory.ts @@ -0,0 +1,74 @@ +// localStorage-backed list of x402 payments this browser has made. Used by +// X402Fetch and the /payment/[txId] detail page. + +const STORAGE_KEY = 'x402_payments'; + +export type PaymentScheme = 'hathor-direct' | 'hathor-direct-upto'; +export type PaymentStatus = 'served' | 'voided'; + +export interface PaymentRecord { + txId: string; + url: string; + route: string; + scheme: PaymentScheme; + amount: string; // atomic units paid + chargedAmount?: string; // upto only + refundAmount?: string; // upto only + refundTxId?: string; // upto only + asset: string; + payTo: string; + payerAddress: string; + network: string; + status: PaymentStatus; + ts: number; +} + +function safeReadAll(): PaymentRecord[] { + if (typeof window === 'undefined') return []; + try { + const raw = window.localStorage.getItem(STORAGE_KEY); + if (!raw) return []; + const parsed = JSON.parse(raw); + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } +} + +function safeWriteAll(records: PaymentRecord[]) { + if (typeof window === 'undefined') return; + try { + window.localStorage.setItem(STORAGE_KEY, JSON.stringify(records)); + } catch { + /* quota or private mode — ignore */ + } +} + +export function getPayments(): PaymentRecord[] { + return safeReadAll(); +} + +export function getPayment(txId: string): PaymentRecord | undefined { + return safeReadAll().find((p) => p.txId === txId); +} + +export function addPayment(record: PaymentRecord): void { + const existing = safeReadAll(); + const i = existing.findIndex((p) => p.txId === record.txId); + if (i >= 0) existing[i] = { ...existing[i], ...record }; + else existing.unshift(record); + safeWriteAll(existing); +} + +export function markVoided(txId: string): void { + const existing = safeReadAll(); + const i = existing.findIndex((p) => p.txId === txId); + if (i < 0) return; + existing[i] = { ...existing[i], status: 'voided' }; + safeWriteAll(existing); +} + +export function clearPayments(): void { + if (typeof window === 'undefined') return; + window.localStorage.removeItem(STORAGE_KEY); +} diff --git a/dapp/lib/toast.tsx b/dapp/lib/toast.tsx new file mode 100644 index 0000000..71205f0 --- /dev/null +++ b/dapp/lib/toast.tsx @@ -0,0 +1,162 @@ +'use client'; + +import { createContext, useContext, useState, useCallback, ReactNode, useEffect } from 'react'; +import { createPortal } from 'react-dom'; + +interface Toast { + id: string; + message: string; + type: 'success' | 'error' | 'info'; +} + +interface ToastContextType { + toasts: Toast[]; + addToast: (message: string, type: Toast['type']) => void; + removeToast: (id: string) => void; +} + +const ToastContext = createContext(undefined); + +export function ToastProvider({ children }: { children: ReactNode }) { + const [toasts, setToasts] = useState([]); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + const addToast = useCallback((message: string, type: Toast['type']) => { + const id = Math.random().toString(36).substring(2, 11); + console.log('[ToastProvider] Adding toast:', { id, message, type }); + setToasts((prev) => { + const newToasts = [...prev, { id, message, type }]; + console.log('[ToastProvider] New toasts array:', newToasts); + return newToasts; + }); + setTimeout(() => { + setToasts((prev) => prev.filter((t) => t.id !== id)); + }, 5000); + }, []); + + const removeToast = useCallback((id: string) => { + setToasts((prev) => prev.filter((t) => t.id !== id)); + }, []); + + const toastContainer = mounted ? ( + createPortal( +
+ {toasts.map((toast) => ( +
+ + {toast.type === 'success' && '✓'} + {toast.type === 'error' && '✕'} + {toast.type === 'info' && 'ℹ'} + + {toast.message} + +
+ ))} +
, + document.body + ) + ) : null; + + return ( + + {children} + {toastContainer} + + ); +} + +export function useToast() { + const context = useContext(ToastContext); + if (!context) { + throw new Error('useToast must be used within ToastProvider'); + } + return context; +} + +export const toast = { + success: (message: string) => { + console.log('[Toast] Dispatching success:', message); + if (typeof window !== 'undefined') { + window.dispatchEvent(new CustomEvent('toast', { detail: { message, type: 'success' } })); + } + }, + error: (message: string) => { + console.log('[Toast] Dispatching error:', message); + if (typeof window !== 'undefined') { + window.dispatchEvent(new CustomEvent('toast', { detail: { message, type: 'error' } })); + } + }, + info: (message: string) => { + console.log('[Toast] Dispatching info:', message); + if (typeof window !== 'undefined') { + window.dispatchEvent(new CustomEvent('toast', { detail: { message, type: 'info' } })); + } + }, +}; + +export function Toaster() { + const { addToast } = useToast(); + + useEffect(() => { + console.log('[Toaster] Setting up event listener'); + const handleToast = (event: Event) => { + const customEvent = event as CustomEvent; + console.log('[Toaster] Received toast event:', customEvent.detail); + addToast(customEvent.detail.message, customEvent.detail.type); + }; + + window.addEventListener('toast', handleToast); + return () => { + console.log('[Toaster] Cleaning up event listener'); + window.removeEventListener('toast', handleToast); + }; + }, [addToast]); + + return null; +} diff --git a/dapp/lib/utils.ts b/dapp/lib/utils.ts new file mode 100644 index 0000000..d21931a --- /dev/null +++ b/dapp/lib/utils.ts @@ -0,0 +1,62 @@ +export function cn(...inputs: (string | undefined | null | false)[]) { + return inputs.filter(Boolean).join(' ') +} + +export const TOKENS = [ + { value: 'HTR', label: 'HTR' }, +]; + +export function formatAddress(address: string): string { + if (!address) return ''; + return `${address.slice(0, 6)}...${address.slice(-4)}`; +} + +export function formatNumber(num: number, decimals: number = 2): string { + return num.toFixed(decimals); +} + +export function formatTokenAmount(amount: bigint | number, decimals: number = 2): string { + if (typeof amount === 'bigint') { + // Convert BigInt to string to avoid precision loss with large numbers + const amountStr = amount.toString(); + + // Pad with zeros if needed (for values less than 100) + const paddedStr = amountStr.padStart(decimals + 1, '0'); + + // Insert decimal point + const decimalPosition = paddedStr.length - decimals; + const integerPart = paddedStr.slice(0, decimalPosition); + const decimalPart = paddedStr.slice(decimalPosition); + + return `${integerPart}.${decimalPart}`; + } + return (amount / 100).toFixed(decimals); +} + +export function formatBalance(balance: bigint | number, decimals: number = 2): string { + if (typeof balance === 'bigint') { + // Convert BigInt to string to avoid precision loss with large numbers + const balanceStr = balance.toString(); + + // Pad with zeros if needed (for values less than 100) + const paddedStr = balanceStr.padStart(decimals + 1, '0'); + + // Insert decimal point + const decimalPosition = paddedStr.length - decimals; + const integerPart = paddedStr.slice(0, decimalPosition); + const decimalPart = paddedStr.slice(decimalPosition); + + return `${integerPart}.${decimalPart}`; + } + return (balance / 100).toFixed(decimals); +} + +export function formatBalanceWithCommas(balance: bigint | number, decimals: number = 2): string { + const formatted = formatBalance(balance, decimals); + const [integerPart, decimalPart] = formatted.split('.'); + + // Add thousand separators + const withCommas = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ','); + + return decimalPart ? `${withCommas}.${decimalPart}` : withCommas; +} diff --git a/dapp/lib/version.ts b/dapp/lib/version.ts new file mode 100644 index 0000000..1774e93 --- /dev/null +++ b/dapp/lib/version.ts @@ -0,0 +1 @@ +export const APP_VERSION = 'v0.2.0'; diff --git a/dapp/lib/walletConnectClient.ts b/dapp/lib/walletConnectClient.ts new file mode 100644 index 0000000..75c999f --- /dev/null +++ b/dapp/lib/walletConnectClient.ts @@ -0,0 +1,29 @@ +import Client from '@walletconnect/sign-client'; +import { WALLETCONNECT_PROJECT_ID, WALLETCONNECT_METADATA, RELAY_URL } from './walletConnectConfig'; + +let client: Client | null = null; + +export async function initializeWalletConnectClient(): Promise { + if (client) { + return client; + } + + try { + client = await Client.init({ + projectId: WALLETCONNECT_PROJECT_ID, + relayUrl: RELAY_URL, + metadata: WALLETCONNECT_METADATA, + }); + return client; + } catch (error) { + console.error('Failed to initialize WalletConnect client:', error); + throw error; + } +} + +export function getWalletConnectClient(): Client { + if (!client) { + throw new Error('WalletConnect client is not initialized'); + } + return client; +} diff --git a/dapp/lib/walletConnectConfig.ts b/dapp/lib/walletConnectConfig.ts new file mode 100644 index 0000000..0f1299f --- /dev/null +++ b/dapp/lib/walletConnectConfig.ts @@ -0,0 +1,11 @@ +export const WALLETCONNECT_PROJECT_ID = process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID || 'YOUR_PROJECT_ID'; + +// Customize these values for your dApp +export const WALLETCONNECT_METADATA = { + name: process.env.NEXT_PUBLIC_APP_NAME || 'Hathor dApp', + description: process.env.NEXT_PUBLIC_APP_DESCRIPTION || 'Decentralized application on Hathor Network', + url: typeof window !== 'undefined' ? window.location.origin : 'https://your-dapp.com', + icons: [typeof window !== 'undefined' ? `${window.location.origin}/images/icon.png` : 'https://your-dapp.com/images/icon.png'], +}; + +export const RELAY_URL = 'wss://relay.walletconnect.com'; diff --git a/dapp/next.config.js b/dapp/next.config.js new file mode 100644 index 0000000..5cd8cc3 --- /dev/null +++ b/dapp/next.config.js @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: 'standalone', +} + +module.exports = nextConfig diff --git a/dapp/next.config.production.js b/dapp/next.config.production.js new file mode 100644 index 0000000..1c8496a --- /dev/null +++ b/dapp/next.config.production.js @@ -0,0 +1,9 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: 'export', + images: { + unoptimized: true, + }, +} + +module.exports = nextConfig diff --git a/dapp/package-lock.json b/dapp/package-lock.json new file mode 100644 index 0000000..b475cd1 --- /dev/null +++ b/dapp/package-lock.json @@ -0,0 +1,11057 @@ +{ + "name": "dapp", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dapp", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@walletconnect/sign-client": "^2.23.0", + "@walletconnect/utils": "^2.23.0", + "@web3modal/standalone": "^2.4.3", + "framer-motion": "^12.23.24", + "next": "14.2.3", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "sonner": "^2.0.7" + }, + "devDependencies": { + "@playwright/test": "^1.56.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "@vitejs/plugin-react": "^5.1.1", + "@vitest/coverage-v8": "^4.0.12", + "@vitest/ui": "^4.0.12", + "autoprefixer": "^10.4.19", + "c8": "^10.1.3", + "eslint": "^8", + "eslint-config-next": "14.2.3", + "happy-dom": "^20.0.10", + "jsdom": "^27.2.0", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.3", + "typescript": "^5", + "vitest": "^4.0.12" + } + }, + "node_modules/@acemir/cssom": { + "version": "0.9.31", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", + "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@adobe/css-tools": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", + "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", + "license": "MIT" + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.2.tgz", + "integrity": "sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^3.0.0", + "@csstools/css-color-parser": "^4.0.1", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.2.5" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", + "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.6" + } + }, + "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz", + "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.2.tgz", + "integrity": "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.1.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.2.tgz", + "integrity": "sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/core/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.5.1.tgz", + "integrity": "sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/reactive-element": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.3.tgz", + "integrity": "sha512-QuTgnG52Poic7uM1AN5yJ09QMe0O28e10XzSvWDz02TJiiKee4stsiownEIadWm8nYzyDAyT+gKzUoZmiWQtsQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.0.0" + } + }, + "node_modules/@motionone/animation": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.18.0.tgz", + "integrity": "sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==", + "license": "MIT", + "dependencies": { + "@motionone/easing": "^10.18.0", + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/animation/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@motionone/dom": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.18.0.tgz", + "integrity": "sha512-bKLP7E0eyO4B2UaHBBN55tnppwRnaE3KFfh3Ps9HhnAkar3Cb69kUCJY9as8LrccVYKgHA+JY5dOQqJLOPhF5A==", + "license": "MIT", + "dependencies": { + "@motionone/animation": "^10.18.0", + "@motionone/generators": "^10.18.0", + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/dom/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@motionone/easing": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.18.0.tgz", + "integrity": "sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg==", + "license": "MIT", + "dependencies": { + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/easing/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@motionone/generators": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.18.0.tgz", + "integrity": "sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg==", + "license": "MIT", + "dependencies": { + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/generators/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@motionone/svelte": { + "version": "10.16.4", + "resolved": "https://registry.npmjs.org/@motionone/svelte/-/svelte-10.16.4.tgz", + "integrity": "sha512-zRVqk20lD1xqe+yEDZhMYgftsuHc25+9JSo+r0a0OWUJFocjSV9D/+UGhX4xgJsuwB9acPzXLr20w40VnY2PQA==", + "license": "MIT", + "dependencies": { + "@motionone/dom": "^10.16.4", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/svelte/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@motionone/types": { + "version": "10.17.1", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.17.1.tgz", + "integrity": "sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A==", + "license": "MIT" + }, + "node_modules/@motionone/utils": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.18.0.tgz", + "integrity": "sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw==", + "license": "MIT", + "dependencies": { + "@motionone/types": "^10.17.1", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/utils/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@motionone/vue": { + "version": "10.16.4", + "resolved": "https://registry.npmjs.org/@motionone/vue/-/vue-10.16.4.tgz", + "integrity": "sha512-z10PF9JV6SbjFq+/rYabM+8CVlMokgl8RFGvieSGNTmrkQanfHn+15XBrhG3BgUfvmTeSeyShfOHpG0i9zEdcg==", + "deprecated": "Motion One for Vue is deprecated. Use Oku Motion instead https://oku-ui.com/motion", + "license": "MIT", + "dependencies": { + "@motionone/dom": "^10.16.4", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/vue/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@msgpack/msgpack": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-3.1.3.tgz", + "integrity": "sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA==", + "license": "ISC", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@next/env": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz", + "integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.3.tgz", + "integrity": "sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "10.3.10" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz", + "integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz", + "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz", + "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz", + "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz", + "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz", + "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz", + "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz", + "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz", + "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.0.tgz", + "integrity": "sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.7.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.0.tgz", + "integrity": "sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.122.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", + "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", + "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", + "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.16.1.tgz", + "integrity": "sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@swc/helpers/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tybys/wasm-util/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.37", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", + "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", + "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/@types/whatwg-mimetype": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz", + "integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz", + "integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", + "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz", + "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz", + "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", + "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.2.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.2.tgz", + "integrity": "sha512-sPK//PHO+kAkScb8XITeB1bf7fsk85Km7+rt4eeuRR3VS1/crD47cmV5wicisJmjNdfeokTZwjMk4Mj2d58Mgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.2", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.2", + "vitest": "4.1.2" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz", + "integrity": "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.2", + "@vitest/utils": "4.1.2", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.2.tgz", + "integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.2", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.2.tgz", + "integrity": "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.2.tgz", + "integrity": "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.2", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.2.tgz", + "integrity": "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.2", + "@vitest/utils": "4.1.2", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.2.tgz", + "integrity": "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.2.tgz", + "integrity": "sha512-/irhyeAcKS2u6Zokagf9tqZJ0t8S6kMZq4ZG9BHZv7I+fkRrYfQX4w7geYeC2r6obThz39PDxvXQzZX+qXqGeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.2", + "fflate": "^0.8.2", + "flatted": "^3.4.2", + "pathe": "^2.0.3", + "sirv": "^3.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "4.1.2" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.2.tgz", + "integrity": "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.2", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@walletconnect/core": { + "version": "2.23.9", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.23.9.tgz", + "integrity": "sha512-ws4WG8LeagUo2ERRo02HryXRcpwIRmCQ3pHLW5gWbVReLXXIpgk6ZAfID3fEGHevIwwnHSGww+nNhNpdXyiq0g==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/jsonrpc-ws-connection": "1.0.16", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "3.0.2", + "@walletconnect/relay-api": "1.0.11", + "@walletconnect/relay-auth": "1.1.0", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.23.9", + "@walletconnect/utils": "2.23.9", + "@walletconnect/window-getters": "1.0.1", + "es-toolkit": "1.44.0", + "events": "3.3.0", + "uint8arrays": "3.1.1" + }, + "engines": { + "node": ">=18.20.8" + } + }, + "node_modules/@walletconnect/core/node_modules/@walletconnect/keyvaluestorage": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.1.1.tgz", + "integrity": "sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==", + "license": "MIT", + "dependencies": { + "@walletconnect/safe-json": "^1.0.1", + "idb-keyval": "^6.2.1", + "unstorage": "^1.9.0" + }, + "peerDependencies": { + "@react-native-async-storage/async-storage": "1.x" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@walletconnect/core/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@walletconnect/core/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@walletconnect/core/node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@walletconnect/core/node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/@walletconnect/environment": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/environment/-/environment-1.0.1.tgz", + "integrity": "sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==", + "license": "MIT", + "dependencies": { + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/events": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/events/-/events-1.0.1.tgz", + "integrity": "sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==", + "license": "MIT", + "dependencies": { + "keyvaluestorage-interface": "^1.0.0", + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/heartbeat": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@walletconnect/heartbeat/-/heartbeat-1.2.2.tgz", + "integrity": "sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==", + "license": "MIT", + "dependencies": { + "@walletconnect/events": "^1.0.1", + "@walletconnect/time": "^1.0.2", + "events": "^3.3.0" + } + }, + "node_modules/@walletconnect/jsonrpc-provider": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-provider/-/jsonrpc-provider-1.0.14.tgz", + "integrity": "sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==", + "license": "MIT", + "dependencies": { + "@walletconnect/jsonrpc-utils": "^1.0.8", + "@walletconnect/safe-json": "^1.0.2", + "events": "^3.3.0" + } + }, + "node_modules/@walletconnect/jsonrpc-types": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.4.tgz", + "integrity": "sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==", + "license": "MIT", + "dependencies": { + "events": "^3.3.0", + "keyvaluestorage-interface": "^1.0.0" + } + }, + "node_modules/@walletconnect/jsonrpc-utils": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.8.tgz", + "integrity": "sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==", + "license": "MIT", + "dependencies": { + "@walletconnect/environment": "^1.0.1", + "@walletconnect/jsonrpc-types": "^1.0.3", + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/jsonrpc-ws-connection": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.16.tgz", + "integrity": "sha512-G81JmsMqh5nJheE1mPst1W0WfVv0SG3N7JggwLLGnI7iuDZJq8cRJvQwLGKHn5H1WTW7DEPCo00zz5w62AbL3Q==", + "license": "MIT", + "dependencies": { + "@walletconnect/jsonrpc-utils": "^1.0.6", + "@walletconnect/safe-json": "^1.0.2", + "events": "^3.3.0", + "ws": "^7.5.1" + } + }, + "node_modules/@walletconnect/logger": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/logger/-/logger-3.0.2.tgz", + "integrity": "sha512-7wR3wAwJTOmX4gbcUZcFMov8fjftY05+5cO/d4cpDD8wDzJ+cIlKdYOXaXfxHLSYeDazMXIsxMYjHYVDfkx+nA==", + "license": "MIT", + "dependencies": { + "@walletconnect/safe-json": "^1.0.2", + "pino": "10.0.0" + } + }, + "node_modules/@walletconnect/relay-api": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@walletconnect/relay-api/-/relay-api-1.0.11.tgz", + "integrity": "sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q==", + "license": "MIT", + "dependencies": { + "@walletconnect/jsonrpc-types": "^1.0.2" + } + }, + "node_modules/@walletconnect/relay-auth": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@walletconnect/relay-auth/-/relay-auth-1.1.0.tgz", + "integrity": "sha512-qFw+a9uRz26jRCDgL7Q5TA9qYIgcNY8jpJzI1zAWNZ8i7mQjaijRnWFKsCHAU9CyGjvt6RKrRXyFtFOpWTVmCQ==", + "license": "MIT", + "dependencies": { + "@noble/curves": "1.8.0", + "@noble/hashes": "1.7.0", + "@walletconnect/safe-json": "^1.0.1", + "@walletconnect/time": "^1.0.2", + "uint8arrays": "^3.0.0" + } + }, + "node_modules/@walletconnect/safe-json": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/safe-json/-/safe-json-1.0.2.tgz", + "integrity": "sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==", + "license": "MIT", + "dependencies": { + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/sign-client": { + "version": "2.23.9", + "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.23.9.tgz", + "integrity": "sha512-Xj+hw4E6mGRyhCdVOT/RMgnG+up/Y3v0ho5PlkVozvXWeVSqHNh9DmjLuU97a7OACoGd/oHBF6g3NVqD7MgCMQ==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@walletconnect/core": "2.23.9", + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/logger": "3.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.23.9", + "@walletconnect/utils": "2.23.9", + "events": "3.3.0" + } + }, + "node_modules/@walletconnect/time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/time/-/time-1.0.2.tgz", + "integrity": "sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==", + "license": "MIT", + "dependencies": { + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/types": { + "version": "2.23.9", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.23.9.tgz", + "integrity": "sha512-IUl1PpD/Dig8IE2OZ9XtjbPohEyOZJ73xs92EDUzoIyzRtfm36g2D340pY3iu3AAdLv1yFiaZafB8Hf8RFze8A==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "3.0.2", + "events": "3.3.0" + } + }, + "node_modules/@walletconnect/types/node_modules/@walletconnect/keyvaluestorage": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.1.1.tgz", + "integrity": "sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==", + "license": "MIT", + "dependencies": { + "@walletconnect/safe-json": "^1.0.1", + "idb-keyval": "^6.2.1", + "unstorage": "^1.9.0" + }, + "peerDependencies": { + "@react-native-async-storage/async-storage": "1.x" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@walletconnect/types/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@walletconnect/types/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@walletconnect/types/node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@walletconnect/types/node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/@walletconnect/utils": { + "version": "2.23.9", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.23.9.tgz", + "integrity": "sha512-C5TltCs8UPypNiteYnKSv8+ZDK2EjVDyXCxN6kA9bkA+j6KGsNIV7l9MUA8WBAvE5Gi5EcBdhD3R9Hpo/1HHqQ==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@msgpack/msgpack": "3.1.3", + "@noble/ciphers": "1.3.0", + "@noble/curves": "1.9.7", + "@noble/hashes": "1.8.0", + "@scure/base": "1.2.6", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "3.0.2", + "@walletconnect/relay-api": "1.0.11", + "@walletconnect/relay-auth": "1.1.0", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.23.9", + "@walletconnect/window-getters": "1.0.1", + "@walletconnect/window-metadata": "1.0.1", + "blakejs": "1.2.1", + "detect-browser": "5.3.0", + "ox": "0.9.3", + "uint8arrays": "3.1.1" + } + }, + "node_modules/@walletconnect/utils/node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@walletconnect/utils/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@walletconnect/utils/node_modules/@walletconnect/keyvaluestorage": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.1.1.tgz", + "integrity": "sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==", + "license": "MIT", + "dependencies": { + "@walletconnect/safe-json": "^1.0.1", + "idb-keyval": "^6.2.1", + "unstorage": "^1.9.0" + }, + "peerDependencies": { + "@react-native-async-storage/async-storage": "1.x" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@walletconnect/utils/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@walletconnect/utils/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@walletconnect/utils/node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@walletconnect/utils/node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/@walletconnect/window-getters": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.1.tgz", + "integrity": "sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==", + "license": "MIT", + "dependencies": { + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/window-metadata": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.1.tgz", + "integrity": "sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==", + "license": "MIT", + "dependencies": { + "@walletconnect/window-getters": "^1.0.1", + "tslib": "1.14.1" + } + }, + "node_modules/@web3modal/core": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@web3modal/core/-/core-2.4.3.tgz", + "integrity": "sha512-7Z/sDe9RIYQ2k9ITcxgEa/u7FvlI76vcVVZn9UY4ISivefqrH4JAS3GX4JmVNUUlovwuiZdyqBv4llAQOMK6Rg==", + "deprecated": "Web3Modal is now Reown AppKit. Please follow the upgrade guide at https://docs.reown.com/appkit/upgrade/from-w3m-to-reown", + "license": "Apache-2.0", + "dependencies": { + "buffer": "6.0.3", + "valtio": "1.10.5" + } + }, + "node_modules/@web3modal/standalone": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@web3modal/standalone/-/standalone-2.4.3.tgz", + "integrity": "sha512-5ATXBoa4GGm+TIUSsKWsfWCJunv1XevOizpgTFhqyeGgRDmWhqsz9UIPzH/1mk+g0iJ/xqMKs5F6v9D2QeKxag==", + "deprecated": "This package has been deprecated in favor of @walletconnect/modal. Please read more at https://docs.walletconnect.com", + "license": "Apache-2.0", + "dependencies": { + "@web3modal/core": "2.4.3", + "@web3modal/ui": "2.4.3" + } + }, + "node_modules/@web3modal/ui": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@web3modal/ui/-/ui-2.4.3.tgz", + "integrity": "sha512-J989p8CdtEhI9gZHf/rZ/WFqYlrAHWw9GmAhFoiNODwjAp0BoG/uoaPiijJMchXdngihZOjLGCQwDXU16DHiKg==", + "deprecated": "Web3Modal is now Reown AppKit. Please follow the upgrade guide at https://docs.reown.com/appkit/upgrade/from-w3m-to-reown", + "license": "Apache-2.0", + "dependencies": { + "@web3modal/core": "2.4.3", + "lit": "2.7.5", + "motion": "10.16.2", + "qrcode": "1.5.3" + } + }, + "node_modules/abitype": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz", + "integrity": "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.0.tgz", + "integrity": "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.27", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", + "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001774", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.2.tgz", + "integrity": "sha512-byD6KPdvo72y/wj2T/4zGEvvlis+PsZsn/yPS3pEO+sFpcrqRpX/TJCxvVaEsNeMrfQbCr7w163YqoD9IYwHXw==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.13", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.13.tgz", + "integrity": "sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/c8": { + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", + "integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.1", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^7.0.1", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "monocart-coverage-reports": "^2" + }, + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001784", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001784.tgz", + "integrity": "sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie-es": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssstyle": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.7.tgz", + "integrity": "sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^4.1.1", + "@csstools/css-syntax-patches-for-csstree": "^1.0.21", + "css-tree": "^3.1.0", + "lru-cache": "^11.2.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cssstyle/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-urls": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.1.tgz", + "integrity": "sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^15.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/data-urls/node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/defu": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.6.tgz", + "integrity": "sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug==", + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, + "node_modules/detect-browser": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.3.0.tgz", + "integrity": "sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.331", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", + "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/encode-utf8": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", + "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==", + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.1.tgz", + "integrity": "sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.1", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "math-intrinsics": "^1.1.0", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-toolkit": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.44.0.tgz", + "integrity": "sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-next": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.3.tgz", + "integrity": "sha512-ZkNztm3Q7hjqvB1rRlOX8P9E/cXRL9ajRcs8jufEtwMfTVYRqnmtnaSu57QqHyBlovMuiB8LEzfLBkh5RYV6Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "14.2.3", + "@rushstack/eslint-patch": "^1.3.3", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.0.0-canary-7118f5dd7-20230705", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz", + "integrity": "sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz", + "integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.38.0", + "motion-utils": "^12.36.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/framer-motion/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/h3": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", + "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.3", + "crossws": "^0.3.5", + "defu": "^6.1.6", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } + }, + "node_modules/happy-dom": { + "version": "20.8.9", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.8.9.tgz", + "integrity": "sha512-Tz23LR9T9jOGVZm2x1EPdXqwA37G/owYMxRwU0E4miurAtFsPMQ1d2Jc2okUaSjZqAFz2oEn3FLXC5a0a+siyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": ">=20.0.0", + "@types/whatwg-mimetype": "^3.0.2", + "@types/ws": "^8.18.1", + "entities": "^7.0.1", + "whatwg-mimetype": "^3.0.0", + "ws": "^8.18.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/happy-dom/node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==", + "license": "MIT" + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/html-encoding-sniffer/node_modules/@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/html-encoding-sniffer/node_modules/@noble/hashes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", + "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/idb-keyval": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", + "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", + "license": "Apache-2.0" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.4.0.tgz", + "integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@acemir/cssom": "^0.9.28", + "@asamuzakjp/dom-selector": "^6.7.6", + "@exodus/bytes": "^1.6.0", + "cssstyle": "^5.3.4", + "data-urls": "^6.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.0", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.1.0", + "ws": "^8.18.3", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/@noble/hashes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", + "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/jsdom/node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/keyvaluestorage-interface": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz", + "integrity": "sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==", + "license": "MIT" + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lit": { + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/lit/-/lit-2.7.5.tgz", + "integrity": "sha512-i/cH7Ye6nBDUASMnfwcictBnsTN91+aBjXoTHF2xARghXScKxpD4F4WYI+VLXg9lqbMinDfvoI7VnZXjyHgdfQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^1.6.0", + "lit-element": "^3.3.0", + "lit-html": "^2.7.0" + } + }, + "node_modules/lit-element": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.3.3.tgz", + "integrity": "sha512-XbeRxmTHubXENkV4h8RIPyr8lXc+Ff28rkcQzw3G6up2xg5E8Zu1IgOWIwBLEQsu3cOVFqdYwiVi0hv0SlpqUA==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.1.0", + "@lit/reactive-element": "^1.3.0", + "lit-html": "^2.8.0" + } + }, + "node_modules/lit-html": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.8.0.tgz", + "integrity": "sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", + "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/motion": { + "version": "10.16.2", + "resolved": "https://registry.npmjs.org/motion/-/motion-10.16.2.tgz", + "integrity": "sha512-p+PurYqfUdcJZvtnmAqu5fJgV2kR0uLFQuBKtLeFVTrYEVllI99tiOTSefVNYuip9ELTEkepIIDftNdze76NAQ==", + "license": "MIT", + "dependencies": { + "@motionone/animation": "^10.15.1", + "@motionone/dom": "^10.16.2", + "@motionone/svelte": "^10.16.2", + "@motionone/types": "^10.15.1", + "@motionone/utils": "^10.15.1", + "@motionone/vue": "^10.16.2" + } + }, + "node_modules/motion-dom": { + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz", + "integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.36.0" + } + }, + "node_modules/motion-utils": { + "version": "12.36.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz", + "integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==", + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/multiformats": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", + "license": "(Apache-2.0 AND MIT)" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz", + "integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==", + "deprecated": "This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details.", + "license": "MIT", + "dependencies": { + "@next/env": "14.2.3", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.3", + "@next/swc-darwin-x64": "14.2.3", + "@next/swc-linux-arm64-gnu": "14.2.3", + "@next/swc-linux-arm64-musl": "14.2.3", + "@next/swc-linux-x64-gnu": "14.2.3", + "@next/swc-linux-x64-musl": "14.2.3", + "@next/swc-win32-arm64-msvc": "14.2.3", + "@next/swc-win32-ia32-msvc": "14.2.3", + "@next/swc-win32-x64-msvc": "14.2.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-mock-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz", + "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ox": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.9.3.tgz", + "integrity": "sha512-KzyJP+fPV4uhuuqrTZyok4DC7vFzi7HLUFiUNEmpbyh59htKWkOC98IONC1zgXJPbHAhQgqs6B0Z6StCGhmQvg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.0.9", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ox/node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ox/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pino": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.0.0.tgz", + "integrity": "sha512-eI9pKwWEix40kfvSzqEP6ldqOoBIN7dwD/o91TY5z8vQI12sAffpR/pOqAD1IVVwIVHDpHjkq0joBPdJD0rafA==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "slow-redact": "^0.3.0", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", + "license": "MIT" + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-import/node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-compare": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.5.1.tgz", + "integrity": "sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz", + "integrity": "sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "encode-utf8": "^1.0.3", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qrcode/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/qrcode/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/qrcode/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qrcode/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, + "node_modules/resolve": { + "version": "2.0.0-next.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", + "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", + "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.122.0", + "@rolldown/pluginutils": "1.0.0-rc.12" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-x64": "1.0.0-rc.12", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", + "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slow-redact": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/slow-redact/-/slow-redact-0.3.2.tgz", + "integrity": "sha512-MseHyi2+E/hBRqdOi5COy6wZ7j7DxXRz9NkseavNYSvvWC06D8a5cidVZX3tcG5eCW3NIyVU4zT63hw0Q486jw==", + "license": "MIT" + }, + "node_modules/sonic-boom": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/sonner": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", + "integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", + "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "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.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.2.tgz", + "integrity": "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^10.2.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/test-exclude/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude/node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/test-exclude/node_modules/glob/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", + "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz", + "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.27" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz", + "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "license": "MIT" + }, + "node_modules/uint8arrays": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.1.1.tgz", + "integrity": "sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==", + "license": "MIT", + "dependencies": { + "multiformats": "^9.4.2" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/valtio": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/valtio/-/valtio-1.10.5.tgz", + "integrity": "sha512-jTp0k63VXf4r5hPoaC6a6LCG4POkVSh629WLi1+d5PlajLsbynTMd7qAgEiOSPxzoX5iNvbN7iZ/k/g29wrNiQ==", + "license": "MIT", + "dependencies": { + "proxy-compare": "2.5.1", + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/vite": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", + "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.12", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.2.tgz", + "integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.2", + "@vitest/mocker": "4.1.2", + "@vitest/pretty-format": "4.1.2", + "@vitest/runner": "4.1.2", + "@vitest/snapshot": "4.1.2", + "@vitest/spy": "4.1.2", + "@vitest/utils": "4.1.2", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.2", + "@vitest/browser-preview": "4.1.2", + "@vitest/browser-webdriverio": "4.1.2", + "@vitest/ui": "4.1.2", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz", + "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/dapp/package.json b/dapp/package.json new file mode 100644 index 0000000..8eca91f --- /dev/null +++ b/dapp/package.json @@ -0,0 +1,62 @@ +{ + "name": "dapp", + "version": "0.1.0", + "description": "A decentralized application built on Hathor Network", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "test": "vitest", + "test:ui": "vitest --ui", + "test:run": "vitest run", + "test:coverage": "vitest run --coverage", + "test:e2e": "playwright test", + "test:e2e:ui": "playwright test --ui" + }, + "dependencies": { + "@walletconnect/sign-client": "^2.23.0", + "@walletconnect/utils": "^2.23.0", + "@web3modal/standalone": "^2.4.3", + "framer-motion": "^12.23.24", + "next": "14.2.3", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "sonner": "^2.0.7" + }, + "devDependencies": { + "@playwright/test": "^1.56.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "@vitejs/plugin-react": "^5.1.1", + "@vitest/coverage-v8": "^4.0.12", + "@vitest/ui": "^4.0.12", + "autoprefixer": "^10.4.19", + "c8": "^10.1.3", + "eslint": "^8", + "eslint-config-next": "14.2.3", + "happy-dom": "^20.0.10", + "jsdom": "^27.2.0", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.3", + "typescript": "^5", + "vitest": "^4.0.12" + }, + "keywords": [ + "hathor", + "hathor-network", + "dapp", + "decentralized", + "nano-contracts", + "blockchain", + "web3", + "metamask-snaps", + "walletconnect" + ], + "author": "", + "license": "MIT" +} diff --git a/dapp/playwright.config.ts b/dapp/playwright.config.ts new file mode 100644 index 0000000..c348ce7 --- /dev/null +++ b/dapp/playwright.config.ts @@ -0,0 +1,43 @@ +import { defineConfig, devices } from '@playwright/test' + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './e2e', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + use: { + baseURL: 'http://localhost:3000', + trace: 'on-first-retry', + screenshot: 'only-on-failure', + }, + + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + ], + + webServer: { + command: 'npm run dev', + url: 'http://localhost:3000', + reuseExistingServer: !process.env.CI, + env: { + NEXT_PUBLIC_USE_MOCK_WALLET: 'true', + NEXT_PUBLIC_DEFAULT_NETWORK: 'testnet', + }, + }, +}) diff --git a/dapp/postcss.config.js b/dapp/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/dapp/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/dapp/public/.gitkeep b/dapp/public/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dapp/public/favicon.png b/dapp/public/favicon.png new file mode 100644 index 0000000..815bd32 Binary files /dev/null and b/dapp/public/favicon.png differ diff --git a/dapp/public/images/icon.png b/dapp/public/images/icon.png new file mode 100644 index 0000000..d445eea Binary files /dev/null and b/dapp/public/images/icon.png differ diff --git a/dapp/scripts/deploy.sh b/dapp/scripts/deploy.sh new file mode 100755 index 0000000..0c34f20 --- /dev/null +++ b/dapp/scripts/deploy.sh @@ -0,0 +1,98 @@ +#!/bin/bash + +# Deployment script for the x402 Hathor dApp. +# Usage: ./scripts/deploy.sh [aws_profile] +# +# Sites: staging, production +# Commands: build, sync, clear_cache + +if [ -z "$1" ] || [ -z "$2" ]; then + echo "Usage: $0 [aws_profile]" + echo "" + echo "Sites: staging, production" + echo "Commands: build, sync, clear_cache" + echo "" + echo "Example: $0 staging build" + echo "Example: $0 production sync my-aws-profile" + exit 1 +fi + +site=$1 +command=$2 +aws_profile=$3 + +# Per-site env. Update before first deploy. +case $site in + staging) + NEXT_PUBLIC_DEFAULT_NETWORK=testnet + NEXT_PUBLIC_HATHOR_NODE_URL_TESTNET=https://node1.testnet.hathor.network/v1a + NEXT_PUBLIC_HATHOR_NODE_URL_MAINNET=https://node1.mainnet.hathor.network/v1a + NEXT_PUBLIC_RESOURCE_SERVER_URL=https://api.x402.hathor.dev + NEXT_PUBLIC_USE_MOCK_WALLET=false + NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_walletconnect_project_id + S3_BUCKET=your-staging-bucket + CLOUDFRONT_ID=your_staging_cloudfront_id + ;; + production) + NEXT_PUBLIC_DEFAULT_NETWORK=mainnet + NEXT_PUBLIC_HATHOR_NODE_URL_TESTNET=https://node1.testnet.hathor.network/v1a + NEXT_PUBLIC_HATHOR_NODE_URL_MAINNET=https://node1.mainnet.hathor.network/v1a + NEXT_PUBLIC_RESOURCE_SERVER_URL=https://api.x402.hathor.dev + NEXT_PUBLIC_USE_MOCK_WALLET=false + NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_walletconnect_project_id + S3_BUCKET=your-production-bucket + CLOUDFRONT_ID=your_production_cloudfront_id + ;; + *) + echo "Unknown site: $site" + echo "Valid sites: staging, production" + exit 1 + ;; +esac + +export NEXT_PUBLIC_DEFAULT_NETWORK +export NEXT_PUBLIC_HATHOR_NODE_URL_TESTNET +export NEXT_PUBLIC_HATHOR_NODE_URL_MAINNET +export NEXT_PUBLIC_RESOURCE_SERVER_URL +export NEXT_PUBLIC_USE_MOCK_WALLET +export NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID +export S3_BUCKET +export CLOUDFRONT_ID + +case $command in + build) + echo "Building for site: $site" + echo " NEXT_PUBLIC_DEFAULT_NETWORK: $NEXT_PUBLIC_DEFAULT_NETWORK" + echo " NEXT_PUBLIC_USE_MOCK_WALLET: $NEXT_PUBLIC_USE_MOCK_WALLET" + echo " NEXT_PUBLIC_RESOURCE_SERVER_URL: $NEXT_PUBLIC_RESOURCE_SERVER_URL" + cp next.config.js next.config.js.bak + cp next.config.production.js next.config.js + npm run build + mv next.config.js.bak next.config.js + ;; + sync) + echo "Syncing for site: $site" + if [ -n "$aws_profile" ]; then + aws s3 sync --delete ./out/ s3://$S3_BUCKET --profile $aws_profile + else + aws s3 sync --delete ./out/ s3://$S3_BUCKET + fi + ;; + clear_cache) + echo "Clearing CloudFront cache for site: $site" + if [ -z "$CLOUDFRONT_ID" ]; then + echo "Warning: CLOUDFRONT_ID not set, skipping cache invalidation" + exit 0 + fi + if [ -n "$aws_profile" ]; then + aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_ID --paths "/*" --profile $aws_profile + else + aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_ID --paths "/*" + fi + ;; + *) + echo "Unknown command: $command" + echo "Valid commands: build, sync, clear_cache" + exit 1 + ;; +esac diff --git a/dapp/tailwind.config.js b/dapp/tailwind.config.js new file mode 100644 index 0000000..792d48e --- /dev/null +++ b/dapp/tailwind.config.js @@ -0,0 +1,24 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + './pages/**/*.{js,ts,jsx,tsx,mdx}', + './components/**/*.{js,ts,jsx,tsx,mdx}', + './app/**/*.{js,ts,jsx,tsx,mdx}', + ], + theme: { + extend: { + colors: { + background: '#0f172a', + foreground: '#f1f5f9', + // Hathor brand colors + 'hathor-orange': '#FF7300', + 'hathor-orange-light': '#FFA600', + 'hathor-green': '#059669', + }, + backgroundImage: { + 'hathor-gradient': 'linear-gradient(244deg, rgb(255, 166, 0) 0%, rgb(255, 115, 0) 100%)', + }, + }, + }, + plugins: [], +} diff --git a/dapp/tsconfig.json b/dapp/tsconfig.json new file mode 100644 index 0000000..2c145a2 --- /dev/null +++ b/dapp/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/dapp/types/hathor.ts b/dapp/types/hathor.ts new file mode 100644 index 0000000..ca5ad3b --- /dev/null +++ b/dapp/types/hathor.ts @@ -0,0 +1,117 @@ +// dApp types for x402 hathor-direct flow. No nano-contract types — those went +// away with the blueprint redesign. + +export interface HathorRPCRequest { + method: string; + params?: any; +} + +export interface HathorRPCResponse { + result?: T; + error?: { + code: number; + message: string; + }; +} + +export interface GetBalanceParams { + network: string; + tokens: string[]; + addressIndexes?: number[]; +} + +export interface GetAddressParams { + network: string; + type: 'first_empty' | 'full_path' | 'index' | 'client'; + full_path?: string; + index?: number; +} + +// --- htr_sendTransaction ----------------------------------------------------- + +export interface SendTransactionOutput { + address?: string; + // The RPC schema requires a digit string (z.string().regex(/^\d+$/)), + // not a number — see hathor-rpc-handler/src/rpcMethods/sendTransaction.ts:59. + value?: string; + token?: string; + timelock?: number; + // OP_RETURN-style data output (we don't use this for hathor-direct but the + // RPC supports it). + type?: 'data'; + data?: string; +} + +export interface SendTransactionInput { + txId: string; + index: number; +} + +export interface SendTransactionParams { + network: string; + outputs: SendTransactionOutput[]; + inputs?: SendTransactionInput[]; + changeAddress?: string; + push_tx: boolean; +} + +// The RPC's response shape varies a bit across wallets (some return a flat +// `hash`, some wrap in `response: { hash }`). We type the union and the +// service helper picks the right field. +export interface SendTransactionResponse { + hash?: string; + txId?: string; + response?: { + hash?: string; + response?: { hash?: string }; + }; + success?: boolean; +} + +// --- htr_getUtxos ------------------------------------------------------------ + +export interface GetUtxosParams { + network: string; + maxUtxos?: number; + token?: string; + filterAddress?: string; + authorities?: number; + amountSmallerThan?: number; + amountBiggerThan?: number; + maximumAmount?: number; + onlyAvailableUtxos?: boolean; +} + +export interface UtxoInfo { + address: string; + amount: string | number | bigint; + tx_id: string; + index: number; + locked: boolean; +} + +export interface UtxoDetails { + total_amount_available: string | number | bigint; + total_utxos_available: string | number | bigint; + total_amount_locked: string | number | bigint; + total_utxos_locked: string | number | bigint; + utxos: UtxoInfo[]; +} + +// --- htr_signWithAddress ----------------------------------------------------- + +export interface SignWithAddressParams { + network: string; + message: string; + addressIndex: number; +} + +export interface SignWithAddressResponse { + message: string; + signature: string; + address: { + address: string; + index: number; + addressPath: string; + }; +} diff --git a/dapp/types/index.ts b/dapp/types/index.ts new file mode 100644 index 0000000..55b31c7 --- /dev/null +++ b/dapp/types/index.ts @@ -0,0 +1,6 @@ +export interface WalletState { + connected: boolean; + address: string | null; + walletBalance: number; + contractBalance: bigint; +} diff --git a/dapp/types/metamask.d.ts b/dapp/types/metamask.d.ts new file mode 100644 index 0000000..fa5fcbf --- /dev/null +++ b/dapp/types/metamask.d.ts @@ -0,0 +1,15 @@ +export {}; + +declare global { + interface Window { + ethereum?: { + isMetaMask?: boolean; + request: (args: { + method: string; + params?: any; + }) => Promise; + on?: (eventName: string, handler: (...args: any[]) => void) => void; + removeListener?: (eventName: string, handler: (...args: any[]) => void) => void; + }; + } +} diff --git a/dapp/types/wallet.ts b/dapp/types/wallet.ts new file mode 100644 index 0000000..c7e6eb0 --- /dev/null +++ b/dapp/types/wallet.ts @@ -0,0 +1,21 @@ +/** + * Wallet Adapter Interface + * This interface defines the contract that all wallet implementations must follow. + * It allows WalletContext to work with any wallet type without knowing the specifics. + */ + +export interface WalletAdapter { + // Connection state + isConnected: boolean; + isInitializing?: boolean; + address: string | null; + + // Connection methods + connect: () => Promise; + disconnect: () => Promise; + + // RPC request method - all wallet operations go through this + request: (method: string, params?: any) => Promise; +} + +export type WalletType = 'walletconnect' | 'metamask' | null; diff --git a/dapp/vitest.config.ts b/dapp/vitest.config.ts new file mode 100644 index 0000000..b4900d8 --- /dev/null +++ b/dapp/vitest.config.ts @@ -0,0 +1,44 @@ +import { defineConfig } from 'vitest/config' +import react from '@vitejs/plugin-react' +import path from 'path' + +export default defineConfig({ + plugins: [react()], + test: { + globals: true, + environment: 'jsdom', + setupFiles: ['./vitest.setup.ts'], + include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + exclude: ['node_modules', 'dist', '.next', 'e2e'], + coverage: { + provider: 'v8', + reporter: ['text', 'json', 'html', 'lcov'], + exclude: [ + 'node_modules/', + 'vitest.setup.ts', + '**/*.d.ts', + '**/*.config.*', + '**/dist/', + '**/.next/', + 'e2e/', + '__mocks__/', + 'types/', + ], + thresholds: { + lines: 80, + functions: 80, + branches: 70, + statements: 80, + }, + }, + }, + resolve: { + alias: { + '@': path.resolve(__dirname, './'), + '@/components': path.resolve(__dirname, './components'), + '@/lib': path.resolve(__dirname, './lib'), + '@/contexts': path.resolve(__dirname, './contexts'), + '@/types': path.resolve(__dirname, './types'), + }, + }, +}) diff --git a/dapp/vitest.setup.ts b/dapp/vitest.setup.ts new file mode 100644 index 0000000..e6ba438 --- /dev/null +++ b/dapp/vitest.setup.ts @@ -0,0 +1,81 @@ +import '@testing-library/jest-dom' +import { cleanup } from '@testing-library/react' +import { afterEach, vi } from 'vitest' + +// Cleanup after each test +afterEach(() => { + cleanup() +}) + +// Mock Next.js router +vi.mock('next/navigation', () => ({ + useRouter: () => ({ + push: vi.fn(), + replace: vi.fn(), + prefetch: vi.fn(), + back: vi.fn(), + pathname: '/', + query: {}, + asPath: '/', + }), + usePathname: () => '/', + useSearchParams: () => new URLSearchParams(), +})) + +// Mock environment variables +process.env.NEXT_PUBLIC_DEFAULT_NETWORK = 'testnet' +process.env.NEXT_PUBLIC_USE_MOCK_WALLET = 'false' +process.env.NEXT_PUBLIC_TESTNET_NODE_URL = 'https://node1.india-testnet.hathor.network/v1a' +process.env.NEXT_PUBLIC_MAINNET_NODE_URL = 'https://node1.mainnet.hathor.network/v1a' +process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID = 'test-project-id' + +// Mock window.matchMedia +Object.defineProperty(window, 'matchMedia', { + writable: true, + value: vi.fn().mockImplementation(query => ({ + matches: false, + media: query, + onchange: null, + addListener: vi.fn(), + removeListener: vi.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })), +}) + +// Mock localStorage +const localStorageMock = (() => { + let store: Record = {} + + return { + getItem: (key: string) => store[key] || null, + setItem: (key: string, value: string) => { + store[key] = value.toString() + }, + removeItem: (key: string) => { + delete store[key] + }, + clear: () => { + store = {} + }, + } +})() + +Object.defineProperty(window, 'localStorage', { + value: localStorageMock, +}) + +// Mock IntersectionObserver +global.IntersectionObserver = class IntersectionObserver { + root = null + rootMargin = '' + thresholds = [] + constructor() {} + disconnect() {} + observe() {} + takeRecords() { + return [] + } + unobserve() {} +} as any diff --git a/data/payments.sqlite b/data/payments.sqlite new file mode 100644 index 0000000..db7a745 Binary files /dev/null and b/data/payments.sqlite differ diff --git a/data/payments.sqlite-shm b/data/payments.sqlite-shm new file mode 100644 index 0000000..b8d9315 Binary files /dev/null and b/data/payments.sqlite-shm differ diff --git a/data/payments.sqlite-wal b/data/payments.sqlite-wal new file mode 100644 index 0000000..272113f Binary files /dev/null and b/data/payments.sqlite-wal differ diff --git a/dedupStore.js b/dedupStore.js new file mode 100644 index 0000000..879c583 --- /dev/null +++ b/dedupStore.js @@ -0,0 +1,142 @@ +// Persistent dedup ledger + blocklist for the verifier. better-sqlite3 gives +// us an atomic `INSERT OR IGNORE` + `SELECT` cycle which is exactly what we +// need to distinguish first-redemption / idempotent retry / replay attempts. +// +// The same DB file is safe to share across the resource-server and the +// (optional) standalone facilitator thanks to WAL mode. + +'use strict'; + +const path = require('path'); +const fs = require('fs'); +const Database = require('better-sqlite3'); + +function openStore(dbPath) { + // Ensure the parent directory exists for first-run docker mounts. + const dir = path.dirname(dbPath); + if (dir && dir !== '.') fs.mkdirSync(dir, { recursive: true }); + + const db = new Database(dbPath); + db.pragma('journal_mode = WAL'); + db.pragma('synchronous = NORMAL'); + + db.exec(` + CREATE TABLE IF NOT EXISTS payments ( + tx_id TEXT NOT NULL, + output_index INTEGER NOT NULL, + request_id TEXT NOT NULL, + payer_address TEXT NOT NULL, + amount INTEGER NOT NULL, + charged_amount INTEGER, + refund_tx_id TEXT, + asset TEXT NOT NULL, + served_at INTEGER NOT NULL, + response_hash TEXT, + voided INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY (tx_id, output_index) + ); + + CREATE INDEX IF NOT EXISTS idx_payments_payer ON payments(payer_address); + + CREATE TABLE IF NOT EXISTS blocklist ( + address TEXT PRIMARY KEY, + reason TEXT NOT NULL, + blocked_at INTEGER NOT NULL, + related_tx TEXT + ); + `); + + const stmts = { + insertIgnore: db.prepare(` + INSERT OR IGNORE INTO payments + (tx_id, output_index, request_id, payer_address, amount, asset, served_at) + VALUES (@txId, @outputIndex, @requestId, @payerAddress, @amount, @asset, @servedAt) + `), + selectByKey: db.prepare( + `SELECT * FROM payments WHERE tx_id = ? AND output_index = ?` + ), + markVoided: db.prepare( + `UPDATE payments SET voided = 1 WHERE tx_id = ? AND output_index = ?` + ), + setResponseHash: db.prepare( + `UPDATE payments SET response_hash = ? WHERE tx_id = ? AND output_index = ?` + ), + setUptoSettlement: db.prepare(` + UPDATE payments + SET charged_amount = ?, refund_tx_id = ? + WHERE tx_id = ? AND output_index = ? + `), + selectBlock: db.prepare(`SELECT 1 FROM blocklist WHERE address = ?`), + insertBlock: db.prepare(` + INSERT OR REPLACE INTO blocklist (address, reason, blocked_at, related_tx) + VALUES (?, ?, ?, ?) + `), + countPayments: db.prepare(`SELECT COUNT(*) AS n FROM payments`), + countBlocks: db.prepare(`SELECT COUNT(*) AS n FROM blocklist`), + }; + + // Atomic redemption. Returns: + // 'new' — first time we're seeing this (txId, outputIndex) + // 'idempotent' — already redeemed against the same requestId; safe replay + // 'conflict' — already redeemed against a DIFFERENT requestId; replay attempt + const markRedeemedIfFreeTx = db.transaction( + ({ txId, outputIndex, requestId, payerAddress, amount, asset }) => { + const info = stmts.insertIgnore.run({ + txId, + outputIndex, + requestId, + payerAddress, + amount: Number(amount), + asset, + servedAt: Math.floor(Date.now() / 1000), + }); + if (info.changes === 1) return { result: 'new' }; + + const existing = stmts.selectByKey.get(txId, outputIndex); + if (!existing) return { result: 'new' }; // shouldn't happen but be safe + if (existing.request_id === requestId) return { result: 'idempotent', existing }; + return { result: 'conflict', existing }; + } + ); + + return { + markRedeemedIfFree(key, fields) { + return markRedeemedIfFreeTx({ ...key, ...fields }); + }, + getRecord(txId, outputIndex) { + return stmts.selectByKey.get(txId, outputIndex) || null; + }, + markVoided(txId, outputIndex) { + stmts.markVoided.run(txId, outputIndex); + }, + attachResponseHash(txId, outputIndex, hash) { + stmts.setResponseHash.run(hash, txId, outputIndex); + }, + attachUptoSettlement(txId, outputIndex, chargedAmount, refundTxId) { + stmts.setUptoSettlement.run( + chargedAmount == null ? null : Number(chargedAmount), + refundTxId || null, + txId, + outputIndex + ); + }, + isBlocked(address) { + return Boolean(stmts.selectBlock.get(address)); + }, + addBlock(address, reason, relatedTx) { + stmts.insertBlock.run(address, String(reason || 'manual'), Math.floor(Date.now() / 1000), relatedTx || null); + }, + stats() { + return { + payments: stmts.countPayments.get().n, + blocked: stmts.countBlocks.get().n, + path: dbPath, + }; + }, + close() { + db.close(); + }, + }; +} + +module.exports = { openStore }; diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..0641e7d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,113 @@ +services: + # The seller's wallet-headless. Used ONLY by the upto scheme to issue refund + # transactions; the exact scheme doesn't touch it. Fund this wallet with HTR + # before running upto demos. + wallet-headless: + image: hathornetwork/hathor-wallet-headless:latest + pull_policy: always + environment: + HEADLESS_HTTP_BIND: "0.0.0.0" + HEADLESS_HTTP_PORT: "8000" + HEADLESS_NETWORK: ${HATHOR_NETWORK:-testnet} + HEADLESS_SERVER: ${FULLNODE_URL:-https://node1.testnet.hathor.network}/v1a/ + HEADLESS_TX_MINING_URL: ${TX_MINING_URL:-https://txmining.testnet.hathor.network/} + # wallet-headless refuses to boot without a default seed. We don't + # actually use the "default" wallet — init-seller-wallet.sh starts the + # "seller" wallet via POST /start with SELLER_SEED at runtime. This is + # the classic all-"abandon" test seed (publicly known, no funds). + HEADLESS_SEED_DEFAULT: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" + entrypoint: ["node", "dist/index.js"] + healthcheck: + test: ["CMD", "node", "-e", "require('http').get('http://localhost:8000/wallet/status',r=>{process.exit(r.statusCode<500?0:1)}).on('error',()=>process.exit(1))"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 15s + + resource-server: + build: . + environment: + FULLNODE_URL: ${FULLNODE_URL:-https://node1.testnet.hathor.network} + HATHOR_NETWORK: ${HATHOR_NETWORK:-testnet} + RESOURCE_SERVER_PORT: "3000" + RESOURCE_SERVER_PUBLIC_URL: ${RESOURCE_SERVER_PUBLIC_URL:-http://localhost:3000} + SELLER_ADDRESS: ${SELLER_ADDRESS:-} + SELLER_WALLET_URL: http://wallet-headless:8000 + SELLER_WALLET_ID: ${SELLER_WALLET_ID:-seller} + SELLER_SEED: ${SELLER_SEED:-} + SERVER_SECRET: ${SERVER_SECRET:-} + REQUEST_ID_TTL_SECONDS: ${REQUEST_ID_TTL_SECONDS:-120} + DEDUP_DB_PATH: /app/data/payments.sqlite + ZERO_CONF_MAX_AMOUNT: ${ZERO_CONF_MAX_AMOUNT:-9007199254740991} + HTR_PAYMENT_AMOUNT: ${HTR_PAYMENT_AMOUNT:-100} + GENERATE_MAX_PRICE: ${GENERATE_MAX_PRICE:-500} + VERIFY_MODE: ${VERIFY_MODE:-self} + FACILITATOR_URL: ${FACILITATOR_URL:-http://facilitator:8402} + command: ["sh", "init-seller-wallet.sh", "node", "resource-server.js"] + ports: + - "${RESOURCE_SERVER_HOST_PORT:-3000}:3000" + volumes: + - x402_data:/app/data + healthcheck: + test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health',r=>{process.exit(r.statusCode<500?0:1)}).on('error',()=>process.exit(1))"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 10s + depends_on: + wallet-headless: + condition: service_healthy + + # Optional standalone facilitator. Off by default. Bring up with: + # docker compose --profile facilitator up + # and set VERIFY_MODE=facilitator on the resource-server. + facilitator: + profiles: ["facilitator"] + build: . + environment: + FULLNODE_URL: ${FULLNODE_URL:-https://node1.testnet.hathor.network} + HATHOR_NETWORK: ${HATHOR_NETWORK:-testnet} + FACILITATOR_PORT: "8402" + SERVER_SECRET: ${SERVER_SECRET:-} + DEDUP_DB_PATH: /app/data/payments.sqlite + ZERO_CONF_MAX_AMOUNT: ${ZERO_CONF_MAX_AMOUNT:-9007199254740991} + command: ["node", "facilitator.js"] + ports: + - "${FACILITATOR_HOST_PORT:-8402}:8402" + volumes: + - x402_data:/app/data + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8402/health"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 10s + + dapp: + build: + context: ./dapp + dockerfile: Dockerfile + args: + NEXT_PUBLIC_USE_MOCK_WALLET: ${NEXT_PUBLIC_USE_MOCK_WALLET:-false} + NEXT_PUBLIC_DEFAULT_NETWORK: ${HATHOR_NETWORK:-testnet} + NEXT_PUBLIC_RESOURCE_SERVER_URL: ${NEXT_PUBLIC_RESOURCE_SERVER_URL:-http://localhost:3000} + NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID: ${NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID:-8264fff563181da658ce64ee80e80458} + NEXT_PUBLIC_HATHOR_NODE_URL_TESTNET: ${NEXT_PUBLIC_HATHOR_NODE_URL_TESTNET:-https://node1.testnet.hathor.network/v1a} + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 15s + ports: + - "${DAPP_HOST_PORT:-4020}:3000" + depends_on: + resource-server: + condition: service_healthy + +# Docker-managed named volume for the verifier's dedup ledger + blocklist +# (SQLite at /app/data/payments.sqlite). Both `resource-server` and the +# optional `facilitator` share it. Persists across container recreations +# and across Dokploy redeploys. +volumes: + x402_data: diff --git a/examples/mcp-server/README.md b/examples/mcp-server/README.md new file mode 100644 index 0000000..6b0674d --- /dev/null +++ b/examples/mcp-server/README.md @@ -0,0 +1,106 @@ +# x402 Payment-Gated MCP Server + +A minimal MCP server whose `get_weather` tool is gated by an x402 `hathor-direct` +payment. AI agents (Claude Code, etc.) call the tool, receive a 402-style +response with payment requirements, send a regular Hathor payment on-chain, +sign the server-issued `requestId` with the payer key, and re-call the tool +with the payment proof. + +No facilitator, no nano contracts, no escrow. + +## What this demonstrates + +- `get_weather` — costs `PRICE_PER_CALL` atomic units (default 100 = 1.00 HTR). +- `get_price` — free; returns the current pricing + a fresh `requestId`. + +## Setup + +```bash +cd examples/mcp-server +npm install +``` + +You'll need: + +- A Hathor address you control (the **seller** address) where payments land +- A 32-byte secret to HMAC `requestId`s with + +## Running + +```bash +FULLNODE_URL=https://node1.testnet.hathor.network \ +HATHOR_NETWORK=testnet \ +SELLER_ADDRESS=WZe3ty22...your_address... \ +SERVER_SECRET=$(openssl rand -hex 32) \ +PRICE_PER_CALL=100 \ +node server.js +``` + +## Using with Claude Code + +Add to your MCP config (`~/.claude/claude_desktop_config.json`): + +```json +{ + "mcpServers": { + "x402-weather": { + "command": "node", + "args": ["/abs/path/to/examples/mcp-server/server.js"], + "env": { + "FULLNODE_URL": "https://node1.testnet.hathor.network", + "HATHOR_NETWORK": "testnet", + "SELLER_ADDRESS": "WZe3ty22...", + "SERVER_SECRET": "..." + } + } + } +} +``` + +## Payment flow + +``` +Agent MCP server Hathor fullnode + | | | + |-- get_weather(city) -------->| | + |<-- 402 + accepts[] ----------| | + | | | + |-- send_tokens(amount,payTo)----------send tx---------->| + | (regular Hathor payment; | + | no contract, no escrow) | + | | | + |-- sign(requestId) ---->[wallet] | + | | | + |-- get_weather(city, payment=base64(payload)) --------->| + | |-- fetch tx, check sig, | + | | dedup, zero-conf safety + | | | + |<-- weather data + payment ---| | +``` + +The `payment` argument is a base64-encoded JSON `PaymentPayload`: + +```jsonc +{ + "x402Version": 2, + "scheme": "hathor-direct", + "network": "hathor:testnet", + "payload": { + "txId": "000abc…", + "payerAddress": "WPo2…", + "signature": "BASE64…", // bitcore.Message signature of requestId + "requestId": "." + } +} +``` + +## Notes + +- Dedup ledger lives at `./data/payments.sqlite` by default. Override with + `DEDUP_DB_PATH`. +- Zero-confirmation is accepted (the verifier rejects voided/conflicting txs + at verify time; a real production deployment would also wire a post-serve + void watcher and a blocklist). +- This example reuses the top-level `verifier.js` / `dedupStore.js` / + `requestId.js` modules. Run `npm install` from the example directory to + pull `better-sqlite3` (needed by the dedup store). diff --git a/examples/mcp-server/package.json b/examples/mcp-server/package.json new file mode 100644 index 0000000..add0d6a --- /dev/null +++ b/examples/mcp-server/package.json @@ -0,0 +1,19 @@ +{ + "name": "x402-mcp-server-example", + "version": "0.2.0", + "description": "Example MCP server with x402 hathor-direct payment-gated tools", + "license": "MIT", + "type": "module", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.12.1", + "better-sqlite3": "^11.5.0", + "@hathor/wallet-lib": "^2.17.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=22" + } +} diff --git a/examples/mcp-server/server.js b/examples/mcp-server/server.js new file mode 100644 index 0000000..cbde596 --- /dev/null +++ b/examples/mcp-server/server.js @@ -0,0 +1,233 @@ +#!/usr/bin/env node + +// x402-gated MCP server — single-tool example. +// +// `get_weather` returns weather data only if the caller includes a valid +// `payment` argument (base64-encoded x402 PaymentPayload pointing at an +// on-chain hathor-direct payment). The server verifies the payment locally +// via the shared verifier — no facilitator, no escrow, no nano contracts. +// +// Run: +// SELLER_ADDRESS=WZe3... SERVER_SECRET=... node server.js +// +// Use with Claude Code: see README. + +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import { z } from 'zod'; +import { createRequire } from 'module'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const require = createRequire(import.meta.url); +const { openStore } = require('../../dedupStore.js'); +const { mintRequestId } = require('../../requestId.js'); +const { verifyDirectPayment } = require('../../verifier.js'); + +// --- config ---------------------------------------------------------------- + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const DEFAULT_DEDUP = path.join(__dirname, 'data', 'payments.sqlite'); + +const cfg = { + fullnodeUrl: process.env.FULLNODE_URL || 'https://node1.testnet.hathor.network', + network: process.env.HATHOR_NETWORK || 'testnet', + sellerAddress: process.env.SELLER_ADDRESS || '', + serverSecret: process.env.SERVER_SECRET || '', + requestIdTtlSeconds: parseInt(process.env.REQUEST_ID_TTL_SECONDS || '120', 10), + htrTokenUid: '00', + pricePerCall: parseInt(process.env.PRICE_PER_CALL || '100', 10), + dedupDbPath: process.env.DEDUP_DB_PATH || DEFAULT_DEDUP, + resourceBase: process.env.MCP_RESOURCE_BASE || 'mcp://x402-mcp-server', +}; + +if (!cfg.sellerAddress) { + // eslint-disable-next-line no-console + console.error('[x402-mcp] WARNING: SELLER_ADDRESS unset — 402 responses will be invalid'); +} +if (!cfg.serverSecret) { + // eslint-disable-next-line no-console + console.error('[x402-mcp] WARNING: SERVER_SECRET unset — requestId verification will reject everything'); +} + +const store = openStore(cfg.dedupDbPath); + +// --- 402 + verify helpers -------------------------------------------------- + +function buildPaymentRequired(toolName) { + const resource = `${cfg.resourceBase}/tool/${toolName}`; + const network = `hathor:${cfg.network}`; + const requestId = mintRequestId( + { + route: resource, + amount: String(cfg.pricePerCall), + payTo: cfg.sellerAddress, + asset: cfg.htrTokenUid, + network, + }, + cfg.serverSecret || 'dev-fallback-secret', + cfg.requestIdTtlSeconds + ); + return { + x402Version: 2, + accepts: [{ + scheme: 'hathor-direct', + network, + resource, + amount: String(cfg.pricePerCall), + asset: cfg.htrTokenUid, + payTo: cfg.sellerAddress, + maxTimeoutSeconds: cfg.requestIdTtlSeconds, + description: `Pay ${(cfg.pricePerCall / 100).toFixed(2)} HTR for ${toolName}`, + extra: { requestId }, + }], + }; +} + +async function verifyPayment(paymentArg, toolName) { + let payload; + try { + payload = typeof paymentArg === 'string' + ? JSON.parse(Buffer.from(paymentArg, 'base64').toString('utf-8')) + : paymentArg; + } catch (_) { + return { valid: false, invalidReason: 'invalid_payment_encoding' }; + } + + const resource = `${cfg.resourceBase}/tool/${toolName}`; + const requirement = { + scheme: 'hathor-direct', + network: `hathor:${cfg.network}`, + amount: String(cfg.pricePerCall), + asset: cfg.htrTokenUid, + payTo: cfg.sellerAddress, + resource, + }; + + return verifyDirectPayment({ + payload, + requirements: requirement, + fullnodeUrl: cfg.fullnodeUrl, + dedupStore: store, + serverSecret: cfg.serverSecret, + zeroConfMaxAmount: Number.MAX_SAFE_INTEGER, + }); +} + +// --- MCP server ------------------------------------------------------------ + +const server = new McpServer({ + name: 'x402-weather', + version: '0.2.0', +}); + +server.tool( + 'get_weather', + 'Get current weather for a city. Costs 1.00 HTR per call via x402 hathor-direct payment.', + { + city: z.string().describe('City name (e.g., "Sao Paulo")'), + payment: z.string().optional().describe('Base64-encoded x402 hathor-direct PaymentPayload (JSON)'), + }, + async ({ city, payment }) => { + if (!payment) { + const requirements = buildPaymentRequired('get_weather'); + return { + content: [{ + type: 'text', + text: JSON.stringify({ + error: 'payment_required', + status: 402, + message: `This tool costs ${(cfg.pricePerCall / 100).toFixed(2)} HTR per call. Include a "payment" argument (base64 JSON) with a hathor-direct PaymentPayload.`, + ...requirements, + }, null, 2), + }], + isError: true, + }; + } + + const verification = await verifyPayment(payment, 'get_weather'); + if (!verification.valid) { + return { + content: [{ + type: 'text', + text: JSON.stringify({ + error: 'payment_invalid', + reason: verification.invalidReason, + ...buildPaymentRequired('get_weather'), + }, null, 2), + }], + isError: true, + }; + } + + const data = { + city, + temperature: Math.round(20 + Math.random() * 15), + humidity: Math.round(40 + Math.random() * 40), + condition: ['Sunny', 'Partly cloudy', 'Cloudy', 'Light rain'][Math.floor(Math.random() * 4)], + wind: `${Math.round(5 + Math.random() * 20)} km/h`, + timestamp: new Date().toISOString(), + }; + + return { + content: [{ + type: 'text', + text: JSON.stringify({ + data, + payment: { + x402Version: 2, + success: true, + scheme: 'hathor-direct', + network: `hathor:${cfg.network}`, + transaction: verification.txId, + payer: verification.payerAddress, + amount: String(verification.amount), + idempotent: !!verification.idempotent, + }, + }, null, 2), + }], + }; + }, +); + +// Free tool: pricing/discovery. +server.tool( + 'get_price', + 'Get the current x402 price + payment requirements for a tool. Free.', + { tool_name: z.string().describe('Tool name to check pricing for') }, + async ({ tool_name }) => { + const requirements = buildPaymentRequired(tool_name); + return { + content: [{ + type: 'text', + text: JSON.stringify({ + tool: tool_name, + price: `${(cfg.pricePerCall / 100).toFixed(2)} HTR`, + network: `hathor:${cfg.network}`, + paymentInfo: requirements, + }, null, 2), + }], + }; + }, +); + +// --- Run ------------------------------------------------------------------- + +async function main() { + const transport = new StdioServerTransport(); + await server.connect(transport); + // eslint-disable-next-line no-console + console.error('[x402-mcp] Running on stdio'); + // eslint-disable-next-line no-console + console.error(`[x402-mcp] Seller: ${cfg.sellerAddress}`); + // eslint-disable-next-line no-console + console.error(`[x402-mcp] Network: ${cfg.network} via ${cfg.fullnodeUrl}`); + // eslint-disable-next-line no-console + console.error(`[x402-mcp] Dedup store: ${cfg.dedupDbPath}`); +} + +main().catch((err) => { + // eslint-disable-next-line no-console + console.error(`[x402-mcp] Fatal: ${err.message}`); + process.exit(1); +}); diff --git a/facilitator.js b/facilitator.js new file mode 100644 index 0000000..fd72954 --- /dev/null +++ b/facilitator.js @@ -0,0 +1,141 @@ +// x402 Facilitator — thin, read-only HTTP wrapper around verifier.js. +// +// This binary is optional in the new design. A resource server with a +// fullnode connection can self-verify (see resource-server.js, VERIFY_MODE). +// The facilitator exists for the case where multiple resource servers want +// to share a dedup/blocklist store, or where a seller wants to outsource the +// verification step. +// +// No wallet. No seed. No nano-contract knowledge. No on-chain settlement. + +'use strict'; + +const express = require('express'); +const config = require('./config'); +const { log } = require('./helpers'); +const { openStore } = require('./dedupStore'); +const { verifyDirectPayment } = require('./verifier'); + +if (!config.serverSecret) { + log('FACILITATOR', 'WARNING: SERVER_SECRET not set — requestId verification will reject everything'); +} + +const store = openStore(config.dedupDbPath); +const app = express(); +app.use(express.json({ limit: '32kb' })); + +// CORS — facilitator is called server-to-server, but allow browsers too for +// development tooling. +app.use((req, res, next) => { + res.header('Access-Control-Allow-Origin', '*'); + res.header('Access-Control-Allow-Headers', 'Content-Type'); + if (req.method === 'OPTIONS') return res.sendStatus(204); + next(); +}); + +function pickRequirement(requirements, scheme) { + if (Array.isArray(requirements)) { + const match = requirements.find((r) => r && r.scheme === scheme); + return match || requirements[0]; + } + return requirements; +} + +app.post('/x402/verify', async (req, res) => { + const { paymentPayload, paymentRequirements } = req.body || {}; + if (!paymentPayload || !paymentRequirements) { + return res.status(400).json({ x402Version: 2, valid: false, invalidReason: 'missing_fields' }); + } + const requirement = pickRequirement(paymentRequirements, paymentPayload.scheme); + + try { + const result = await verifyDirectPayment({ + payload: paymentPayload, + requirements: requirement, + fullnodeUrl: config.fullnodeUrl, + dedupStore: store, + serverSecret: config.serverSecret, + zeroConfMaxAmount: config.zeroConfMaxAmount, + }); + log('FACILITATOR', `verify ${result.valid ? 'PASSED' : 'FAILED'}${result.invalidReason ? `: ${result.invalidReason}` : ''}${result.idempotent ? ' (idempotent)' : ''}`); + return res.json({ x402Version: 2, ...result }); + } catch (err) { + log('FACILITATOR', `verify error: ${err.message}`); + return res.status(500).json({ x402Version: 2, valid: false, invalidReason: 'verifier_error' }); + } +}); + +// Settle is a no-op for hathor-direct: the payment is already on-chain. We +// re-verify (which is idempotent thanks to the dedup row) and echo a v2-style +// settlement response. +app.post('/x402/settle', async (req, res) => { + const { paymentPayload, paymentRequirements } = req.body || {}; + if (!paymentPayload || !paymentRequirements) { + return res.status(400).json({ x402Version: 2, success: false, errorReason: 'missing_fields' }); + } + const requirement = pickRequirement(paymentRequirements, paymentPayload.scheme); + + try { + const result = await verifyDirectPayment({ + payload: paymentPayload, + requirements: requirement, + fullnodeUrl: config.fullnodeUrl, + dedupStore: store, + serverSecret: config.serverSecret, + zeroConfMaxAmount: config.zeroConfMaxAmount, + }); + if (!result.valid) { + return res.json({ + x402Version: 2, + success: false, + errorReason: result.invalidReason, + transaction: '', + network: requirement.network, + }); + } + return res.json({ + x402Version: 2, + success: true, + scheme: paymentPayload.scheme, + network: requirement.network, + transaction: result.txId, + amount: String(result.amount), + payer: result.payerAddress, + }); + } catch (err) { + return res.status(500).json({ + x402Version: 2, + success: false, + errorReason: 'verifier_error', + transaction: '', + network: requirement && requirement.network, + }); + } +}); + +// Discovery — advertise the scheme/network pairs we'll handle. +app.get('/supported', (_req, res) => { + res.json({ + x402Version: 2, + kinds: [ + { x402Version: 2, scheme: 'hathor-direct', network: `hathor:${config.network}` }, + { x402Version: 2, scheme: 'hathor-direct-upto', network: `hathor:${config.network}` }, + ], + extensions: [], + }); +}); + +app.get('/health', (_req, res) => { + res.json({ + status: 'healthy', + network: config.network, + fullnodeUrl: config.fullnodeUrl, + dedup: store.stats(), + uptime: process.uptime(), + }); +}); + +app.listen(config.facilitatorPort, () => { + log('FACILITATOR', `Running on :${config.facilitatorPort} — network=${config.network}, fullnode=${config.fullnodeUrl}`); + log('FACILITATOR', `Dedup store: ${config.dedupDbPath}`); +}); diff --git a/helpers.js b/helpers.js new file mode 100644 index 0000000..da5f503 --- /dev/null +++ b/helpers.js @@ -0,0 +1,126 @@ +// Small utilities shared across the backend: structured logging, fullnode +// reads, tx introspection, and bitcore-style message-signature verification. + +'use strict'; + +const config = require('./config'); + +function log(component, msg) { + // eslint-disable-next-line no-console + console.log(`[${component}] ${msg}`); +} + +// --- Fullnode HTTP ----------------------------------------------------------- + +async function fullnodeRequest(path) { + const url = `${config.fullnodeUrl}/v1a${path}`; + const resp = await fetch(url); + return resp.json(); +} + +// GET /v1a/transaction?id= +// Returns { success, tx?, meta? } where meta has voided_by / conflict_with / first_block. +async function getTransaction(txId) { + return fullnodeRequest(`/transaction?id=${encodeURIComponent(txId)}`); +} + +// Poll until the tx is visible at our fullnode. We don't wait for first_block — +// the verifier is the one that decides whether zero-conf is OK for the amount. +async function waitForTxInMempool(txId, { maxAttempts = 30, intervalMs = 1000 } = {}) { + for (let i = 0; i < maxAttempts; i++) { + try { + const resp = await getTransaction(txId); + if (resp.success && resp.tx) return resp; + } catch (_) { /* swallow and retry */ } + await new Promise((r) => setTimeout(r, intervalMs)); + } + throw new Error(`tx ${txId} not visible at fullnode after ${maxAttempts} polls`); +} + +// --- Transaction introspection ---------------------------------------------- + +// Resolve an output's token UID by looking at token_data + the tx.tokens list. +// HTR is '00' (token_data 0). Custom tokens use the low 7 bits of token_data as +// an index into tx.tokens (1-based). The high bit marks authorities. +function resolveTokenUid(output, tx) { + const td = output.token_data || 0; + const tokenIndex = td & 0x7f; + if (tokenIndex === 0) return '00'; + const entry = (tx.tokens || [])[tokenIndex - 1]; + return entry ? entry.uid : null; +} + +// Find the FIRST output that pays `payTo` at least `minValue` of `asset`. +// Returns { outputIndex, value, token } or null. +function findPayingOutput(tx, { payTo, asset, minValue }) { + const min = BigInt(minValue); + const outputs = tx.outputs || []; + for (let i = 0; i < outputs.length; i++) { + const out = outputs[i]; + const decoded = out.decoded || {}; + if (decoded.address !== payTo) continue; + const token = resolveTokenUid(out, tx); + if (token !== asset) continue; + const value = BigInt(out.value); + if (value < min) continue; + return { outputIndex: i, value: out.value, token }; + } + return null; +} + +function getInputAddresses(tx) { + return new Set((tx.inputs || []).map((i) => i.decoded && i.decoded.address).filter(Boolean)); +} + +function getOutputAddresses(tx) { + return new Set((tx.outputs || []).map((o) => o.decoded && o.decoded.address).filter(Boolean)); +} + +// --- Message-signature verification (Bitcoin-compatible / Hathor) ----------- +// We go through @hathor/wallet-lib's verifyMessage so the address-version-byte +// handling is aligned with how the wallet stack signs. The lib lazily +// initialises network state; we pass the configured network. +let _walletLib = null; +function lazyWalletLib() { + if (_walletLib) return _walletLib; + _walletLib = require('@hathor/wallet-lib'); + // Set the global network so bitcore's address parsing knows which version + // byte to expect. The lib exports a Network class and a default config. + try { + const net = new _walletLib.Network(config.network); + if (_walletLib.config && typeof _walletLib.config.setNetwork === 'function') { + _walletLib.config.setNetwork(net); + } + } catch (_) { /* lib version variance — verifyMessage still works */ } + return _walletLib; +} + +function verifyMessageSignature(message, signatureBase64, address) { + const lib = lazyWalletLib(); + // wallet-lib v2 exposes the crypto helpers under `cryptoUtils.*`; older + // versions used `utils.crypto.*`. Support either. + const verify = + (lib.cryptoUtils && lib.cryptoUtils.verifyMessage) || + (lib.utils && lib.utils.crypto && lib.utils.crypto.verifyMessage) || + (lib.verifyMessage && lib.verifyMessage.bind(lib)); + if (typeof verify !== 'function') { + throw new Error('verifyMessage not available in @hathor/wallet-lib'); + } + try { + return Boolean(verify(message, signatureBase64, address)); + } catch (_) { + return false; + } +} + +module.exports = { + log, + fullnodeRequest, + getTransaction, + waitForTxInMempool, + resolveTokenUid, + findPayingOutput, + getInputAddresses, + getOutputAddresses, + verifyMessageSignature, +}; diff --git a/init-seller-wallet.sh b/init-seller-wallet.sh new file mode 100755 index 0000000..f0876b3 --- /dev/null +++ b/init-seller-wallet.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# Boot-time helper for the seller's wallet-headless. Waits for the headless +# service to come up, then POST /start with the seed so the rest of the stack +# can hit it. The seller wallet is only used by the upto scheme (to issue +# refund txs); the exact scheme never needs it. + +set -e + +HEADLESS_URL="${SELLER_WALLET_URL:-http://wallet-headless:8000}" +WALLET_ID="${SELLER_WALLET_ID:-seller}" + +if [ -z "${SELLER_SEED:-}" ]; then + echo "[init-seller-wallet] SELLER_SEED is empty — skipping (upto refunds will fail)" + exec "$@" +fi + +echo "[init-seller-wallet] Waiting for headless at $HEADLESS_URL ..." +until curl -sf "$HEADLESS_URL/wallet/status" > /dev/null 2>&1; do + sleep 2 +done +echo "[init-seller-wallet] headless is up" + +echo "[init-seller-wallet] Starting wallet '$WALLET_ID' ..." +curl -s -X POST "$HEADLESS_URL/start" \ + -H "Content-Type: application/json" \ + -d "{\"wallet-id\":\"$WALLET_ID\",\"seed\":\"$SELLER_SEED\"}" \ + || echo "[init-seller-wallet] /start returned non-zero (already started?)" + +echo "" +echo "[init-seller-wallet] Waiting a few seconds for sync ..." +sleep 5 +echo "[init-seller-wallet] Ready. Handing off." + +exec "$@" diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..cbb86e1 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1651 @@ +{ + "name": "x402-poc", + "version": "0.2.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "x402-poc", + "version": "0.2.0", + "license": "MIT", + "dependencies": { + "@hathor/wallet-lib": "^2.17.0", + "better-sqlite3": "^11.5.0", + "express": "^4.21.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@hathor/wallet-lib": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@hathor/wallet-lib/-/wallet-lib-2.17.0.tgz", + "integrity": "sha512-PhPkv3kCPWX0gXrJTVlA50GfY64kGPmNxqy0XmOuA77tLm2TzvkZTuho/IlPYoaACQZLV2aXGTCJPqTq9eGtMQ==", + "license": "MIT", + "dependencies": { + "axios": "1.7.7", + "bitcore-lib": "8.25.10", + "bitcore-mnemonic": "8.25.10", + "buffer": "6.0.3", + "crypto-js": "4.2.0", + "isomorphic-ws": "5.0.0", + "lodash": "4.17.21", + "queue-microtask": "1.2.3", + "ws": "8.17.1", + "zod": "3.23.8" + }, + "engines": { + "node": ">=22.0.0", + "npm": ">=10.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/base-x": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz", + "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bech32": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.3.tgz", + "integrity": "sha512-yuVFUvrNcoJi0sv5phmqc6P+Fl1HjRDRNOOkHY2X/3LBy2bIGNSFx4fZ95HMaXHupuS7cZR15AsvtmCIF4UEyg==", + "license": "MIT" + }, + "node_modules/better-sqlite3": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz", + "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bitcore-lib": { + "version": "8.25.10", + "resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-8.25.10.tgz", + "integrity": "sha512-MyHpSg7aFRHe359RA/gdkaQAal3NswYZTLEuu0tGX1RGWXAYN9i/24fsjPqVKj+z0ua+gzAT7aQs0KiKXWCgKA==", + "license": "MIT", + "dependencies": { + "bech32": "=1.1.3", + "bn.js": "=4.11.8", + "bs58": "^4.0.1", + "buffer-compare": "=1.1.1", + "elliptic": "^6.5.3", + "inherits": "=2.0.1", + "lodash": "^4.17.20" + } + }, + "node_modules/bitcore-mnemonic": { + "version": "8.25.10", + "resolved": "https://registry.npmjs.org/bitcore-mnemonic/-/bitcore-mnemonic-8.25.10.tgz", + "integrity": "sha512-FeXxO37BLV5JRvxPmVFB91zRHalavV8H4TdQGt1/hz0AkoPymIV68OkuB+TptpjeYgatcgKPoPvPhglJkTzFQQ==", + "license": "MIT", + "dependencies": { + "bitcore-lib": "^8.25.10", + "unorm": "^1.4.1" + }, + "peerDependencies": { + "bitcore-lib": "^8.20.1" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bl/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "license": "MIT" + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "license": "MIT", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-compare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-compare/-/buffer-compare-1.1.1.tgz", + "integrity": "sha512-O6NvNiHZMd3mlIeMDjP6t/gPG75OqGPeiRZXoMQZJ6iy9GofCls4Ijs5YkPZZwoysizLiedhticmdyx/GyHghA==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", + "license": "MIT" + }, + "node_modules/elliptic/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hash.js/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-errors/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "license": "MIT" + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.92.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", + "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readable-stream/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unorm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", + "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==", + "license": "MIT or GPL-2.0", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..223ca4f --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "x402-poc", + "version": "0.2.0", + "description": "x402 Payment Protocol PoC for Hathor Network — hathor-direct scheme (regular UTXO payments, no nano contracts)", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/HathorNetwork/x402-poc.git" + }, + "scripts": { + "facilitator": "node facilitator.js", + "resource-server": "node resource-server.js", + "start": "node resource-server.js", + "client": "node client.js" + }, + "dependencies": { + "@hathor/wallet-lib": "^2.17.0", + "better-sqlite3": "^11.5.0", + "express": "^4.21.0" + }, + "engines": { + "node": ">=22" + } +} diff --git a/refundIssuer.js b/refundIssuer.js new file mode 100644 index 0000000..9891c31 --- /dev/null +++ b/refundIssuer.js @@ -0,0 +1,64 @@ +// hathor-direct-upto refund: after the resource server determines the actual +// usage, it asks the seller's wallet-headless to send `paid - charged` back to +// the payer. Fire-and-forget; if it fails we surface refundError to the caller. +// +// We talk to the same POST /wallet/send-tx endpoint the dApp would have used. +// The seller wallet must be loaded and funded with HTR (or the relevant token) +// ahead of time — see init-seller-wallet.sh + docker-compose.yml. + +'use strict'; + +async function issueRefund({ + sellerWalletUrl, + sellerWalletId, + refundAddress, + amount, + asset, + log, +}) { + if (!sellerWalletUrl) throw new Error('refundIssuer: sellerWalletUrl not configured'); + if (!sellerWalletId) throw new Error('refundIssuer: sellerWalletId not configured'); + if (!refundAddress) throw new Error('refundIssuer: refundAddress missing'); + const value = Number(amount); + if (!Number.isFinite(value) || value <= 0) { + throw new Error(`refundIssuer: invalid amount ${amount}`); + } + + const output = { address: refundAddress, value }; + // HTR is '00' implicitly; for custom tokens we must set token. + if (asset && asset !== '00') output.token = asset; + + const body = { outputs: [output] }; + + if (log) log('REFUND', `POST ${sellerWalletUrl}/wallet/send-tx — ${value} ${asset || '00'} → ${refundAddress}`); + + const resp = await fetch(`${sellerWalletUrl}/wallet/send-tx`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Wallet-Id': sellerWalletId, + }, + body: JSON.stringify(body), + }); + + let payload; + try { + payload = await resp.json(); + } catch (_) { + throw new Error(`refund tx call returned non-JSON (status ${resp.status})`); + } + + if (!resp.ok || payload.success === false) { + const msg = payload && (payload.error || payload.message) + || `HTTP ${resp.status}`; + throw new Error(`refund tx failed: ${msg}`); + } + + const txId = payload.hash || payload.tx_id || (payload.tx && payload.tx.hash) || null; + if (!txId) throw new Error('refund tx accepted but no txId in response'); + + if (log) log('REFUND', `refund tx broadcast — ${txId}`); + return { txId }; +} + +module.exports = { issueRefund }; diff --git a/requestId.js b/requestId.js new file mode 100644 index 0000000..d92ea64 --- /dev/null +++ b/requestId.js @@ -0,0 +1,81 @@ +// Stateless server-issued challenge token. Encodes the route/amount/payTo/asset +// commitment and an expiry; HMAC-SHA256 binds it to this server's secret so the +// server can verify a returned challenge without keeping any per-challenge state. + +'use strict'; + +const crypto = require('crypto'); + +function b64urlEncode(buf) { + return Buffer.from(buf).toString('base64url'); +} + +function b64urlDecode(str) { + return Buffer.from(str, 'base64url'); +} + +function hmac(secret, body) { + return crypto.createHmac('sha256', secret).update(body).digest(); +} + +// Mint a fresh requestId. `claims` is the canonical 402 challenge: a server +// promise to itself that it offered this exact deal. +function mintRequestId({ route, amount, payTo, asset, network }, secret, ttlSeconds) { + if (!secret) throw new Error('mintRequestId: missing server secret'); + const now = Math.floor(Date.now() / 1000); + const claims = { + route: String(route), + amount: String(amount), + payTo: String(payTo), + asset: String(asset), + network: String(network), + exp: now + Number(ttlSeconds || 60), + nonce: crypto.randomBytes(8).toString('base64url'), + }; + const body = b64urlEncode(JSON.stringify(claims)); + const mac = b64urlEncode(hmac(secret, body)); + return `${body}.${mac}`; +} + +// Verify the MAC, expiry, and that the claims match what the route expects. +// Returns { ok: true, claims } on success or { ok: false, reason } on failure. +function verifyAndDecodeRequestId(token, secret, expected = {}) { + if (!token || typeof token !== 'string') return { ok: false, reason: 'malformed_request_id' }; + const dot = token.indexOf('.'); + if (dot < 0) return { ok: false, reason: 'malformed_request_id' }; + const body = token.slice(0, dot); + const mac = token.slice(dot + 1); + if (!body || !mac) return { ok: false, reason: 'malformed_request_id' }; + + let recomputed; + try { + recomputed = b64urlEncode(hmac(secret, body)); + } catch (_) { + return { ok: false, reason: 'bad_request_id_mac' }; + } + if (recomputed.length !== mac.length || + !crypto.timingSafeEqual(Buffer.from(recomputed), Buffer.from(mac))) { + return { ok: false, reason: 'bad_request_id_mac' }; + } + + let claims; + try { + claims = JSON.parse(b64urlDecode(body).toString('utf-8')); + } catch (_) { + return { ok: false, reason: 'malformed_request_id' }; + } + + if (typeof claims.exp !== 'number' || claims.exp * 1000 < Date.now()) { + return { ok: false, reason: 'request_id_expired' }; + } + + for (const k of ['route', 'amount', 'payTo', 'asset', 'network']) { + if (expected[k] != null && String(expected[k]) !== String(claims[k])) { + return { ok: false, reason: `requestId_${k}_mismatch` }; + } + } + + return { ok: true, claims }; +} + +module.exports = { mintRequestId, verifyAndDecodeRequestId }; diff --git a/resource-server.js b/resource-server.js new file mode 100644 index 0000000..757be5f --- /dev/null +++ b/resource-server.js @@ -0,0 +1,359 @@ +// x402 Resource Server — sells two routes: +// GET /weather — hathor-direct (exact) +// GET /generate — hathor-direct-upto (max-authorize, refund remainder) +// +// Self-verifies via verifier.js by default (VERIFY_MODE=self) so a public +// facilitator is optional. When VERIFY_MODE=facilitator it POSTs to the +// configured facilitator URL instead. + +'use strict'; + +const express = require('express'); +const config = require('./config'); +const { log } = require('./helpers'); +const { openStore } = require('./dedupStore'); +const { mintRequestId } = require('./requestId'); +const { verifyDirectPayment } = require('./verifier'); +const { watchForVoid } = require('./voidWatcher'); +const { issueRefund } = require('./refundIssuer'); + +if (!config.sellerAddress) { + log('RESOURCE-SERVER', 'WARNING: SELLER_ADDRESS not set — 402 responses will be invalid'); +} +if (!config.serverSecret) { + log('RESOURCE-SERVER', 'WARNING: SERVER_SECRET not set — requestIds will be unverifiable'); +} + +const store = openStore(config.dedupDbPath); +const app = express(); +app.use(express.json({ limit: '32kb' })); + +// CORS — open for the dApp + curl tests. +app.use((req, res, next) => { + res.header('Access-Control-Allow-Origin', '*'); + res.header('Access-Control-Allow-Headers', 'Content-Type, PAYMENT-SIGNATURE'); + res.header('Access-Control-Expose-Headers', 'PAYMENT-RESPONSE'); + if (req.method === 'OPTIONS') return res.sendStatus(204); + next(); +}); + +// --------------------------------------------------------------------------- +// 402 building +// --------------------------------------------------------------------------- + +function networkId() { + return `hathor:${config.network}`; +} + +function buildAccept(req, routeConfig) { + const resourceUrl = `${config.resourceServerPublicUrl}${req.path}`; + const amount = String(routeConfig.mode === 'upto' ? routeConfig.maxPrice : routeConfig.price); + const scheme = routeConfig.mode === 'upto' ? 'hathor-direct-upto' : 'hathor-direct'; + + // Mint a fresh, server-issued challenge token. The HMAC commitments include + // the route, amount, and payTo so a payment claim later can't be redirected. + // config.serverSecret always has a value (env or fixed dev fallback) so + // mint+verify use the same key. + const requestId = mintRequestId( + { + route: resourceUrl, + amount, + payTo: config.sellerAddress, + asset: config.htrTokenUid, + network: networkId(), + }, + config.serverSecret, + config.requestIdTtlSeconds + ); + + const description = routeConfig.mode === 'upto' + ? `Pay up to ${(routeConfig.maxPrice / 100).toFixed(2)} HTR — billed by actual usage, remainder refunded` + : `Pay ${(routeConfig.price / 100).toFixed(2)} HTR`; + + return { + scheme, + network: networkId(), + amount, + asset: config.htrTokenUid, + payTo: config.sellerAddress, + resource: resourceUrl, + maxTimeoutSeconds: config.requestIdTtlSeconds, + description, + extra: { + requestId, + facilitatorUrl: config.verifyMode === 'facilitator' ? config.facilitatorUrl : undefined, + }, + }; +} + +function build402Body(req, routeConfig) { + return { + x402Version: 2, + accepts: [buildAccept(req, routeConfig)], + }; +} + +// --------------------------------------------------------------------------- +// Verify — either local (verifier.js) or via the standalone facilitator +// --------------------------------------------------------------------------- + +async function runVerify(paymentPayload, requirement) { + if (config.verifyMode === 'facilitator') { + const resp = await fetch(`${config.facilitatorUrl}/x402/verify`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ paymentPayload, paymentRequirements: requirement }), + }); + return resp.json(); + } + return verifyDirectPayment({ + payload: paymentPayload, + requirements: requirement, + fullnodeUrl: config.fullnodeUrl, + dedupStore: store, + serverSecret: config.serverSecret, + zeroConfMaxAmount: config.zeroConfMaxAmount, + }); +} + +// Handlers call `setUptoChargedAmount(res, n)` from inside the route to tell +// the middleware what actually to charge (in atomic units). Ignored by the +// exact scheme. +function setUptoChargedAmount(res, amount) { + res.locals.x402UptoCharged = amount; +} + +// --------------------------------------------------------------------------- +// Middleware +// --------------------------------------------------------------------------- + +function x402Middleware(routeConfig) { + return async function middleware(req, res, next) { + const signatureHeader = req.headers['payment-signature']; + + if (!signatureHeader) { + log('RESOURCE-SERVER', `402 — no PAYMENT-SIGNATURE for ${req.path}`); + return res.status(402).json(build402Body(req, routeConfig)); + } + + let payment; + try { + payment = JSON.parse(Buffer.from(signatureHeader, 'base64').toString('utf-8')); + } catch (_) { + return res.status(400).json({ error: 'invalid_payment_signature_header' }); + } + + // Re-issue a requestId-bound requirement so the verifier checks against + // the canonical route values, not anything the client could spoof. + const requirement = (() => { + const accept = buildAccept(req, routeConfig); + // Verifier uses what the payload claims and re-checks the HMAC; we just + // pass the canonical route/payTo/amount/asset/network back in. + return { + scheme: accept.scheme, + network: accept.network, + amount: accept.amount, + asset: accept.asset, + payTo: accept.payTo, + resource: accept.resource, + }; + })(); + + log('RESOURCE-SERVER', `verifying ${payment.scheme} for ${req.path}`); + let verification; + try { + verification = await runVerify(payment, requirement); + } catch (err) { + log('RESOURCE-SERVER', `verify error: ${err.message}`); + return res.status(502).json({ error: 'verify_failed', reason: err.message }); + } + + if (!verification.valid) { + log('RESOURCE-SERVER', `payment INVALID: ${verification.invalidReason}`); + return res.status(402).json({ + error: 'payment_invalid', + reason: verification.invalidReason, + ...build402Body(req, routeConfig), + }); + } + + log('RESOURCE-SERVER', `payment VALID — tx=${verification.txId} idempotent=${!!verification.idempotent}`); + res.locals.x402 = { payment, verification, routeConfig, requirement }; + + // Hook res.json to settle (refund) AFTER the handler has produced a body + // so /generate can call setUptoChargedAmount() to specify actual usage. + const originalJson = res.json.bind(res); + res.json = async function patched(body) { + try { + const { paymentResponse } = await finalizeUpto(res); + if (paymentResponse) { + res.set('PAYMENT-RESPONSE', Buffer.from(JSON.stringify(paymentResponse)).toString('base64')); + } + // Start the void watcher only after we've committed to the response + // (so a verification failure doesn't accidentally arm one). + if (!verification.idempotent) { + watchForVoid({ + txId: verification.txId, + outputIndex: verification.outputIndex, + payerAddress: verification.payerAddress, + store, + log, + }); + } + return originalJson({ data: body, payment: paymentResponse }); + } catch (err) { + log('RESOURCE-SERVER', `settlement failed: ${err.message}`); + res.status(500); + return originalJson({ error: 'settlement_failed', reason: err.message }); + } + }; + + next(); + }; +} + +async function finalizeUpto(res) { + const ctx = res.locals.x402; + const { payment, verification, routeConfig } = ctx; + const scheme = payment.scheme; + const paidAmount = Number(verification.paidAmount); + + // Exact scheme — no refund, simple PAYMENT-RESPONSE. + if (scheme === 'hathor-direct') { + return { + paymentResponse: { + x402Version: 2, + success: true, + scheme, + network: `hathor:${config.network}`, + transaction: verification.txId, + amount: String(paidAmount), + payer: verification.payerAddress, + }, + }; + } + + // Upto scheme — clamp charged amount and issue a refund tx if there's + // remainder. Handler should have called setUptoChargedAmount(res, n). + let charged = Number(res.locals.x402UptoCharged); + if (!Number.isFinite(charged) || charged <= 0) charged = paidAmount; // default to full + if (charged > paidAmount) charged = paidAmount; + const refund = paidAmount - charged; + + let refundTxId = null; + let refundError = null; + + if (refund > 0) { + try { + const r = await issueRefund({ + sellerWalletUrl: config.sellerWalletUrl, + sellerWalletId: config.sellerWalletId, + refundAddress: verification.payerAddress, + amount: refund, + asset: verification.asset, + log, + }); + refundTxId = r.txId; + } catch (err) { + refundError = err.message; + log('RESOURCE-SERVER', `refund FAILED: ${err.message}`); + } + } + + // Best-effort persistence of the upto settlement on the dedup row. + try { + store.attachUptoSettlement( + verification.txId, + verification.outputIndex, + charged, + refundTxId + ); + } catch (_) { /* don't block the response on accounting */ } + + return { + paymentResponse: { + x402Version: 2, + success: true, + scheme, + network: `hathor:${config.network}`, + transaction: verification.txId, + amount: String(paidAmount), + chargedAmount: String(charged), + refundAmount: String(refund), + refundTxId, + refundError, + payer: verification.payerAddress, + }, + }; +} + +// --------------------------------------------------------------------------- +// Routes +// --------------------------------------------------------------------------- + +// Exact: GET /weather +app.get( + '/weather', + x402Middleware({ mode: 'exact', price: config.htrPaymentAmount }), + async (_req, res) => { + res.json({ + location: 'Sao Paulo, Brazil', + temperature: 28, + humidity: 65, + condition: 'Partly cloudy', + wind: '12 km/h NE', + forecast: 'Warm with chance of afternoon showers', + timestamp: new Date().toISOString(), + }); + } +); + +// Upto: GET /generate — simulated LLM with variable usage +const GENERATE_PRICE_PER_TOKEN = 1; // 0.01 HTR per token + +app.get( + '/generate', + x402Middleware({ mode: 'upto', maxPrice: config.generateMaxPrice }), + async (req, res) => { + const maxTokens = Math.floor(config.generateMaxPrice / GENERATE_PRICE_PER_TOKEN); + const tokensUsed = Math.floor(50 + Math.random() * Math.max(1, maxTokens - 50)); + const actualCost = Math.max(1, tokensUsed * GENERATE_PRICE_PER_TOKEN); + + // Tell the middleware what to actually charge. + setUptoChargedAmount(res, actualCost); + + const prompt = req.query.prompt || 'default prompt'; + res.json({ + prompt, + completion: `Generated response for: "${prompt}". This mock simulates variable-length LLM output billed per token.`, + usage: { + tokensUsed, + pricePerToken: GENERATE_PRICE_PER_TOKEN, + authorizedMaxAtomic: config.generateMaxPrice, + actualChargedAtomic: actualCost, + }, + timestamp: new Date().toISOString(), + }); + } +); + +app.get('/health', (_req, res) => { + res.json({ + status: 'healthy', + seller: config.sellerAddress, + verifyMode: config.verifyMode, + network: config.network, + dedup: store.stats(), + uptime: process.uptime(), + }); +}); + +app.listen(config.resourceServerPort, () => { + log('RESOURCE-SERVER', `Running on :${config.resourceServerPort}`); + log('RESOURCE-SERVER', `Seller address: ${config.sellerAddress || '(unset)'}`); + log('RESOURCE-SERVER', `Verify mode: ${config.verifyMode}${config.verifyMode === 'facilitator' ? ` (${config.facilitatorUrl})` : ''}`); + log('RESOURCE-SERVER', `Dedup store: ${config.dedupDbPath}`); + log('RESOURCE-SERVER', `Routes:`); + log('RESOURCE-SERVER', ` GET /weather — exact: ${(config.htrPaymentAmount / 100).toFixed(2)} HTR`); + log('RESOURCE-SERVER', ` GET /generate — upto: up to ${(config.generateMaxPrice / 100).toFixed(2)} HTR (usage-billed)`); +}); diff --git a/skills/x402-pay/README.md b/skills/x402-pay/README.md new file mode 100644 index 0000000..f456479 --- /dev/null +++ b/skills/x402-pay/README.md @@ -0,0 +1,130 @@ +# x402-pay — Claude Code skill + +A Claude Code skill that handles HTTP 402 payments on Hathor Network. When a +Claude session makes a request that returns `402 Payment Required` from an +x402-enabled API (or the user explicitly asks to fetch a paid URL), this skill +takes over: it sets up a wallet-headless instance (starting one in Docker if +needed), pays the on-chain HTR, signs the server's `requestId` proof, retries +the request, and surfaces the resource. No browser, no popups, no human +clicking anything beyond yes/no setup prompts. + +## What it solves + +x402 (Coinbase/Cloudflare's HTTP 402 payment protocol) is built for machines +paying machines. The browser dApp at `https://x402.hathor.dev/` works for human +demos, but the real use case is agents making programmatic paid calls. This +skill is the agent-side companion of the resource server at +`https://api.x402.hathor.dev/`. + +## Requirements + +- **Claude Code** with skills support. +- **Docker** installed on the machine (only if you don't already run a + `hathor-wallet-headless` instance). +- **`hathor-wallet-headless`** ≥ the version that ships `POST /wallet/sign-message` + (see "Endpoint dependency" below). +- A **Hathor wallet seed** funded with HTR on the right network. Testnet + funds come from . + +## Endpoint dependency + +This skill uses `POST /wallet/sign-message` on the headless to sign the +`requestId` per input address of the broadcast payment tx. That endpoint was +added to `hathor-wallet-headless` for x402 specifically — PR `#TBD` upstream. +If your headless is older than the release that includes it, the skill aborts +at the signing step with a clear message pointing at the version requirement. + +## Install + +Copy or symlink this directory under `~/.claude/skills/`: + +```bash +# from the repo root +ln -s "$PWD/skills/x402-pay" ~/.claude/skills/x402-pay +``` + +Claude Code picks it up on next session start. To verify: + +``` +Inside Claude Code: /x402-pay +``` + +It should respond by initialising the skill (Step 1 — establish wallet +backend). + +## Trigger conditions + +The skill is invoked two ways: + +1. **Auto-trigger.** Claude detects a `402` status with an `accepts[]` body in + any tool result and considers this skill relevant. It asks the user + whether to proceed before doing anything on-chain. +2. **Explicit slash command.** The user types `/x402-pay` to force the skill + to load right now. Useful if you want to pre-warm the wallet setup before + any 402 hits. + +## Quick start (recommended demo) + +1. Get testnet HTR for your wallet: send `>= 5 HTR` to address-0 of your + wallet's seed. +2. In a Claude Code session, type: + + > "Fetch `https://api.x402.hathor.dev/weather` and tell me what the + > weather is in São Paulo." + +3. Claude makes the request, sees the 402, asks if you want to use the + x402-pay skill. Say yes. +4. First time only, Claude asks about your wallet: + - Is the headless running? (No → spin up via Docker.) + - What's the seed? (Pasted in chat for the demo, or pointed at a file.) +5. The skill pays, signs, retries, and surfaces the weather JSON. Claude + summarises. + +Subsequent calls in the same session skip Step 4 entirely. After the first +successful run, the skill offers to save the headless URL + wallet-id to +memory so future sessions also skip the setup. + +## Try the upto flow + +The resource server at `api.x402.hathor.dev` exposes a `/generate` route +configured as `hathor-direct-upto`. Try: + +> "Fetch `https://api.x402.hathor.dev/generate` and show me the response." + +You'll see: +- The 402's `amount` advertises the MAX (5.00 HTR), not a fixed price. +- The skill pays the full max. +- The response body includes `payment.chargedAmount` (actual usage) and + `payment.refundAmount` (remainder being refunded), with `payment.refundTxId` + pointing at the on-chain refund tx the seller broadcast back to you. +- The skill surfaces all of that to you. + +## Security notes + +- The **seed is never written to memory**. The skill takes it once, uses it + to start the headless, and forgets it. +- The skill **refuses to run cross-network**. A testnet wallet against a + mainnet API (or vice versa) aborts before any on-chain action. +- The skill **never auto-confirms mainnet usage**. If you opt into mainnet at + setup time, the prompt makes the risk explicit. +- The deployment at `headless.x402.hathor.dev` from the live PoC is publicly + reachable — convenient for ops parity during testnet demos but **not** a + pattern to copy in production. Real deployments should keep the headless + loopback-only. + +## File layout + +``` +skills/x402-pay/ +├── SKILL.md — the skill instructions Claude reads +└── README.md — this file (human-facing install + demo guide) +``` + +## Related + +- Resource server at `https://api.x402.hathor.dev/` — + source at `../../resource-server.js`. +- Reference dApp at `https://x402.hathor.dev/` — + source at `../../dapp/`. +- Spec discussion + design proposal at + `/Users/pedroferreira/Hathor/x402/x402-hathor-no-nc-proposal.md`. diff --git a/skills/x402-pay/SKILL.md b/skills/x402-pay/SKILL.md new file mode 100644 index 0000000..ddefb97 --- /dev/null +++ b/skills/x402-pay/SKILL.md @@ -0,0 +1,355 @@ +--- +name: x402-pay +description: Pay for HTTP 402 responses on Hathor Network. Use when a fetch/curl/HTTP request returns 402 Payment Required from an x402-enabled API (e.g., api.x402.hathor.dev), or when the user asks you to fetch a paid URL on Hathor. Orchestrates payment via a `hathor-wallet-headless` instance (starts one in Docker if not running). +metadata: + author: hathor-network + version: "0.1.0" +--- + +# x402-pay — pay HTTP 402 responses on Hathor + +You're driving an x402 payment for the user. The protocol is: +**fetch → 402 + payment requirements → pay on-chain → sign requestId → retry with proof → resource**. + +The wallet backend is `hathor-wallet-headless` running locally (Docker). The user **never sees a popup** — you handle the whole flow conversationally. Confirm intent once; ask for setup info only on the first call of the session; remember answers via `Memory` so subsequent calls skip prompts. + +## Inputs you need by the time you're done with Step 1 + +- `HEADLESS_URL` — **always ask the user; never default silently** (port 8000 is commonly taken on dev machines) +- `WALLET_ID` — **ask the user**; suggested defaults are `agent` or `default` +- `HEADLESS_NETWORK` — must match the 402's `network` field +- a wallet seed — only on cold start + +Save these to memory (`Memory` tool) at the end of a successful run so the next session skips Step 1. + +--- + +## Step 0 — Confirm intent (only on auto-trigger) + +If the user explicitly invoked you (e.g., `/x402-pay`), skip this step. + +Otherwise, when you see a 402 from a tool result, announce: + +> "I see this returned 402 Payment Required (`X.XX HTR` to `Wxxx…`). Want me to pay it with the x402-pay skill?" + +Only proceed on an explicit yes. + +--- + +## Step 1 — Establish the wallet backend + +**Ask first; do not probe-and-guess.** The skill MUST NOT: +- Assume `localhost:8000` silently (the port may be in use by another app). +- Run `docker ps`, read project compose files, or otherwise sniff the local environment to find a headless. +- Inspect the current working directory's repo to infer which wallet the user wants. + +Defaults appear only as suggestions inside `AskUserQuestion` options. + +### 1.0 — Check memory first + +If a previous session saved an `x402-pay-config` memory (HEADLESS_URL, WALLET_ID, HEADLESS_NETWORK), reuse those values and skip ahead to **Step 1.2** to confirm they're still reachable. + +### 1.1 — Ask the user where the headless is + +Use `AskUserQuestion` with two questions: + +**Question 1** — "Where is your hathor-wallet-headless?" +- "http://localhost:8000" *(common default; only correct if nothing else is on that port)* +- "I don't have one — start a local container in Docker" + +If neither option fits, the user picks "Other" and types a URL (e.g., `http://10.0.0.5:8000`). + +**Question 2** — "Wallet ID to use?" +- "agent" +- "default" + +If they pick "Other", they type their own wallet-id. + +If the user picked "start a local container in Docker" for Question 1, branch to **Step 1.4** instead. Otherwise continue. + +### 1.2 — Validate the URL is really a headless + +Don't trust `/health`: it's a 200-OK on many web services that aren't a headless (e.g., TmuxDeck on port 8000). Hit a headless-specific route and check the response is JSON: + +```bash +curl -fsS -H "Accept: application/json" \ + "${HEADLESS_URL}/wallet/status" \ + -H "X-Wallet-Id: ${WALLET_ID}" \ + -o /tmp/x402-status.txt -w "%{http_code} %{content_type}\n" +head -c 400 /tmp/x402-status.txt +``` + +Three things can come back: + +- **Body starts with `<` or content-type is `text/html`** → the URL isn't a headless. Tell the user clearly: + > "`` returned HTML, not JSON — it's serving a different app (port 8000 is often used by other tools). What's the right URL of your hathor-wallet-headless?" + + Re-ask Question 1 from Step 1.1. Do **not** scan the local environment to find another candidate. + +- **JSON with `statusCode: 3`** → wallet is READY. Go to **Step 1.6**. + +- **JSON with `error`/`message` saying the wallet isn't started, or HTTP 404** → URL is a headless but this wallet-id isn't loaded yet. Go to **Step 1.3** to start it. + +### 1.3 — Start a wallet on the existing headless + +Use `AskUserQuestion` for the seed material: +- **Paste seed** (24 words) — for testnet PoC only +- **Path to a seed file** — use `Read` to load it +- **Use a pre-configured `seedKey`** (if their headless has one) + +**Use the value the user actually supplied** — do not copy any literal value (`"default"`, `"test"`, etc.) from examples elsewhere in this skill. Match the right branch below: + +```bash +# Branch A — user pasted a 24-word seed (or you read it from a file): +curl -fsS -X POST "${HEADLESS_URL}/start" \ + -H "Content-Type: application/json" \ + -d "$(jq -nc --arg wid "${WALLET_ID}" --arg seed "${SEED}" \ + '{"wallet-id":$wid, seed:$seed}')" + +# Branch B — user supplied a seedKey configured in their headless: +curl -fsS -X POST "${HEADLESS_URL}/start" \ + -H "Content-Type: application/json" \ + -d "$(jq -nc --arg wid "${WALLET_ID}" --arg sk "${SEED_KEY}" \ + '{"wallet-id":$wid, seedKey:$sk}')" +``` + +The `SEED_KEY` value is **whatever the user typed** in the `AskUserQuestion` answer. If they said "test", send `"seedKey":"test"`. Never substitute "default" unless the user explicitly chose "default". + +If the response is `{success: false, error: "..."}`, surface the error verbatim and stop. Common one: `Invalid wallet seedKey` means the key isn't configured in the headless's settings — re-ask Step 1.1 for the right key or switch to the paste-seed branch. + +Then run the polling block from Step 1.7. On READY, go to **Step 1.6**. + +### 1.4 — Start a local headless in Docker + +Use `AskUserQuestion` for the network: +- **testnet** (default for demos) +- **mainnet** (extra warning: this signs real on-chain payments) +- **abort** + +Then ask for the **host port** to bind. Default 8000 is commonly taken on dev machines (TmuxDeck, other dev servers). Suggest: +- **8000** *(only if you know nothing else is on it)* +- **8765** *(safe random high port)* + +Bind the chosen port and set `HEADLESS_URL=http://localhost:${HOST_PORT}` accordingly. Then ask for the seed (same options as 1.3) and: + +```bash +docker run -d --name x402-headless \ + -p ${HOST_PORT}:8000 \ + -e HEADLESS_NETWORK="${NETWORK}" \ + -e HEADLESS_SERVER="${FULLNODE_URL}" \ + -e HEADLESS_SEED_DEFAULT="${SEED}" \ + hathornetwork/hathor-wallet-headless:latest +``` + +Where `FULLNODE_URL` is: +- `https://node1.testnet.hathor.network/v1a/` for testnet +- `https://node1.mainnet.hathor.network/v1a/` for mainnet + +Poll `${HEADLESS_URL}/wallet/status -H "X-Wallet-Id: ..."` until it returns JSON (max 30s); use the same JSON-vs-HTML check as Step 1.2 to confirm we're talking to a headless we just started, not something else that happened to be on the port. The `HEADLESS_SEED_DEFAULT` env var automatically registers a seed under the key `default`, but the wallet under that key is **not** auto-started — you still need to POST `/start` with `seedKey: "default"` (the literal string `"default"` is correct here because the Docker spin-up registers the seed under exactly that key — *not* a value the user picked): + +```bash +curl -fsS -X POST "${HEADLESS_URL}/start" \ + -H "Content-Type: application/json" \ + -d "$(jq -nc --arg wid "${WALLET_ID:-agent}" '{"wallet-id":$wid, seedKey:"default"}')" +``` + +Then run the polling block from Step 1.7. On READY, go to **Step 1.6**. + +### 1.7 — Wait for `statusCode === 3` (READY) + +**Run this exactly once as a single Bash call.** Do not fire it as multiple parallel curls — concurrent Bash calls in the same response cancel each other on the first non-zero exit. + +```bash +for i in $(seq 1 60); do + STATUS=$(curl -fsS -H "Accept: application/json" \ + "${HEADLESS_URL}/wallet/status" -H "X-Wallet-Id: ${WALLET_ID}" 2>/dev/null \ + | jq -r .statusCode 2>/dev/null) + if [ "$STATUS" = "3" ]; then + echo "READY" + exit 0 # <-- explicit clean exit; do NOT rely on loop fall-through + fi + sleep 1 +done +echo "TIMEOUT statusCode=$STATUS" +exit 1 +``` + +If this prints `READY` and exits 0, the wallet is ready — **do not re-poll, do not double-check in parallel**. Go straight to Step 1.6. + +If it prints `TIMEOUT`, tell the user and stop. Don't retry; the wallet is in a bad state and needs operator attention. + +### 1.6 — Network match check + +Get the headless's configured network: + +```bash +curl -fsS "${HEADLESS_URL}/wallet/status" -H "X-Wallet-Id: ${WALLET_ID}" | jq -r .network +``` + +Compare to the 402's `accepts[0].network` (e.g., `hathor:testnet`). The headless reports a bare name (`testnet`, `mainnet`); strip the `hathor:` prefix from the 402's value before comparing. + +If they don't match, **abort with a clear error**: + +> "Your headless wallet is on `mainnet` but the API at `` advertises `hathor:testnet`. Refusing to pay across networks. Either point the skill at a testnet wallet or refresh the URL." + +No transaction is broadcast. Do not proceed. + +--- + +## Step 2 — Pay + +Parse the 402's `accepts[0]`. You need: +- `amount` (string, atomic units) +- `asset` (token UID, `"00"` for HTR) +- `payTo` (address string) +- `scheme` (`hathor-direct` or `hathor-direct-upto`) +- `network` (e.g., `hathor:testnet`) +- `extra.requestId` (HMAC-signed challenge token) + +### 2.1 — Send the payment tx + +```bash +curl -fsS -X POST "${HEADLESS_URL}/wallet/send-tx" \ + -H "Content-Type: application/json" \ + -H "X-Wallet-Id: ${WALLET_ID}" \ + -d "$(jq -nc --arg addr "${PAY_TO}" --argjson val ${AMOUNT_INT} --arg tok "${ASSET}" '{ + outputs: [{ address: $addr, value: $val, token: $tok }] + }')" +``` + +The `value` field must be an **integer** (atomic units), not a string. The response has `{ success: true, hash: "" }`. Capture `hash` as `TX_ID`. + +If `success` is false, surface the error and stop. Common cause: insufficient HTR. Tell the user to fund the wallet and retry. + +### 2.2 — Fetch the broadcast tx and extract input addresses + +```bash +curl -fsS "${HEADLESS_URL}/wallet/transaction?id=${TX_ID}" -H "X-Wallet-Id: ${WALLET_ID}" \ + | jq -r '[.inputs[].decoded.address] | unique | .[]' +``` + +That gives you the **unique** input addresses, one per line. Save them into a shell array (or process iteratively). + +### 2.3 — Sign requestId once per unique input address + +For each `addr` in the unique-inputs set: + +```bash +curl -fsS -X POST "${HEADLESS_URL}/wallet/sign-message" \ + -H "Content-Type: application/json" \ + -H "X-Wallet-Id: ${WALLET_ID}" \ + -d "$(jq -nc --arg msg "${REQUEST_ID}" --arg addr "${addr}" '{ + message: $msg, + address: $addr + }')" +``` + +The response is `{ success: true, signature: "BASE64…", address: "Wxxx…", index: N }`. Accumulate the `{address, signature}` pairs into a JSON array `SIGNATURES`. + +> Endpoint requirement: `POST /wallet/sign-message` was added to `hathor-wallet-headless` in version ≥ X.Y.Z (the upstream PR motivated by this skill). If you get a 404 here, the user's headless is too old — point them at the version requirement in this skill's README. + +### 2.4 — Assemble the PAYMENT-SIGNATURE header and retry + +Build the payload: + +```bash +PAYLOAD=$(jq -nc \ + --arg scheme "${SCHEME}" \ + --arg network "${NETWORK_HEADER}" \ + --arg txId "${TX_ID}" \ + --argjson sigs "${SIGNATURES}" \ + --arg requestId "${REQUEST_ID}" \ + '{ + x402Version: 2, + scheme: $scheme, + network: $network, + payload: { txId: $txId, signatures: $sigs, requestId: $requestId } + }') + +PAYMENT_SIG=$(printf '%s' "${PAYLOAD}" | base64 -w0) + +curl -fsS "${URL}" -H "PAYMENT-SIGNATURE: ${PAYMENT_SIG}" +``` + +`NETWORK_HEADER` is the 402's `network` field verbatim (e.g., `hathor:testnet`). +`SCHEME` is the 402's `scheme` field. + +Order matters in the `signatures` array: the **first** entry's address is the canonical payer (used by the server for blocklist and — for upto — refund target). If the user has a preference for which address gets the refund, put it first; otherwise the wallet's natural ordering is fine. + +If the response is 200, the body is `{ data: , payment: }`. Return `data` to the user. + +If the response is 402 again, surface the error reason from the body (`reason` or `error` field) and stop. Common reasons: +- `request_id_expired` — the 402's `extra.requestId` is past its TTL. Re-fetch the URL to get a fresh 402, then redo Step 2. +- `bad_signature:` — one of the signatures didn't verify. The headless's sign-message must have a bug, or the wallet's keys changed; retry once. +- `missing_signature_for_input:` — we missed an input address. Step 2.2 should have caught all; double-check the unique set. +- `replay` — the payment was already redeemed. (Unlikely on first try; means the wallet's send-tx returned a known txId, maybe a no-op tx.) + +--- + +## Step 3 — Surface the result + +### For `scheme: hathor-direct` +Print the response's `data` field to the user. Mention the txId briefly: + +> "Paid `0.XX HTR` via tx ``. Here's the response: ..." + +### For `scheme: hathor-direct-upto` +The response body includes `payment.chargedAmount`, `payment.refundAmount`, `payment.refundTxId`. Show the user a summary: + +> "Authorized up to **5.00 HTR**, actually charged **1.20 HTR**, refunded **3.80 HTR** (refund tx ``). Here's the response: ..." + +Then print `data`. + +The refund tx is on-chain and may not yet be confirmed by the time you respond. It will land within seconds on testnet; the user's wallet will pick it up automatically. + +--- + +## Step 4 — Remember setup (offer once) + +If everything succeeded and the user hasn't already opted in, ask: + +> "Want me to remember `HEADLESS_URL=`, `WALLET_ID=`, and `HEADLESS_NETWORK=` so the next x402 payment in a new session skips the setup prompts?" + +If yes, save a project memory via the `Memory` tool: + +``` +--- +name: x402-pay-config +description: Saved wallet-headless config for x402-pay skill (auto-detected on first 402) +metadata: + type: project +--- + +HEADLESS_URL: +WALLET_ID: +HEADLESS_NETWORK: + +(Write the actual values here, not the placeholders.) +``` + +Do **not** save the seed under any circumstances. The seed should only ever be passed at start-time and the user re-types/re-points-to it on each new headless boot. + +--- + +## Error handling — quick reference + +| Where | Symptom | What to do | +|---|---|---| +| Step 1.4 | `docker: command not found` | Tell user to install Docker, or re-run Step 1.1 with the URL of an existing headless | +| Step 1.4 | `port is already allocated` | Re-prompt for a different host port (suggest 8765 or 9000) and adjust `HEADLESS_URL` | +| Step 1.2 | URL returns HTML | The URL isn't a headless. Re-prompt Step 1.1 for the right URL; do **not** scan docker/compose to guess | +| Step 2.1 | `{success: false, error: "Insufficient amount of HTR..."}` | Tell user the wallet is underfunded, print the wallet's address-0 (`curl /wallet/address?index=0`), suggest the testnet faucet | +| Step 2.2 | tx not found at headless | Headless may not have indexed the tx yet — retry once after 2s | +| Step 2.3 | `POST /wallet/sign-message` returns 404 | Headless version is too old; mention the version requirement | +| Step 2.3 | `{success: false, error: "address does not belong to the wallet"}` | A tx input came from outside the wallet — that means we're talking to the wrong wallet. Recheck `WALLET_ID` | +| Step 2.4 | `request_id_expired` | Re-fetch the URL for a fresh requestId, then redo from Step 2 (the on-chain tx can be reused with a new signature over the new requestId) | + +--- + +## Notes on what this skill **does not** do + +- It does not run on mainnet without an explicit prompt + warning. +- It does not save the seed to memory. +- It does not try to recover from a hung headless beyond a single retry. If the user's wallet is in a weird state (`statusCode` 1 or 2 for minutes), tell them to restart it. +- It does not retry a failed payment indefinitely. One attempt per 402. +- It does not support payment channels (those don't exist in the current `hathor-direct` scheme). diff --git a/verifier.js b/verifier.js new file mode 100644 index 0000000..5f2f326 --- /dev/null +++ b/verifier.js @@ -0,0 +1,173 @@ +// The single source of truth for "is this hathor-direct payment claim valid?" +// Imported by resource-server.js (self-verify) and wrapped over HTTP by +// facilitator.js. No wallet, no nano contract — just fullnode reads plus a +// SQLite dedup/blocklist file. + +'use strict'; + +const { + getTransaction, + findPayingOutput, + getInputAddresses, + verifyMessageSignature, +} = require('./helpers'); +const { verifyAndDecodeRequestId } = require('./requestId'); + +function fail(reason) { + return { valid: false, invalidReason: reason }; +} + +// `requirements` is one PaymentRequirements (object) describing the route the +// client is trying to satisfy: { scheme, network, amount, asset, payTo, resource }. +// `payload` is the decoded PAYMENT-SIGNATURE payload from the client. +// +// Payload shape (multi-signature; no legacy single-sig back-compat): +// { +// x402Version: 2, scheme, network, +// payload: { +// txId, +// signatures: [{address, signature}, ...], // one entry per unique tx input address +// requestId, +// } +// } +// +// The verifier requires every unique input address in the tx to be present in +// `signatures` with a signature that verifies over `requestId`. That proves the +// claimant controls every funding source of the payment. The "canonical payer" +// (for blocklist / dedup record / upto refund target) is the address of the +// FIRST entry in `signatures` — client controls the ordering. +async function verifyDirectPayment({ + payload, + requirements, + fullnodeUrl, + dedupStore, + serverSecret, + zeroConfMaxAmount = Number.MAX_SAFE_INTEGER, +}) { + if (!payload || typeof payload !== 'object') return fail('missing_payload'); + const inner = payload.payload || {}; + const { txId, signatures, requestId } = inner; + if (!txId || !requestId) return fail('missing_payload_fields'); + if (!Array.isArray(signatures) || signatures.length === 0) { + return fail('missing_signatures'); + } + + // Canonical payer = first signature's address. Used for blocklist, dedup + // record and upto refund recipient. + const firstSig = signatures[0]; + if (!firstSig || typeof firstSig.address !== 'string' || typeof firstSig.signature !== 'string') { + return fail('malformed_signatures'); + } + const payerAddress = firstSig.address; + + // 1) requestId — HMAC + expiry + commitment match against the route. + const decoded = verifyAndDecodeRequestId(requestId, serverSecret, { + route: requirements.resource, + amount: requirements.amount, + payTo: requirements.payTo, + asset: requirements.asset, + network: requirements.network, + }); + if (!decoded.ok) return fail(decoded.reason); + + // 2) Fetch the tx from the fullnode. (verifier.js doesn't need a wallet — + // this is what makes the facilitator stateless and seedless.) + let resp; + try { + resp = await getTransaction(txId); + } catch (err) { + return fail(`fullnode_error: ${err.message}`); + } + if (!resp || !resp.success || !resp.tx) return fail('tx_not_found'); + const { tx, meta } = resp; + + // 3) An output of the right token must pay `payTo` at least `amount`. + const paying = findPayingOutput(tx, { + payTo: requirements.payTo, + asset: requirements.asset, + minValue: requirements.amount, + }); + if (!paying) return fail('no_paying_output'); + + // 4) Build a map of {address → signature} from the payload. Reject + // duplicate addresses with conflicting signatures (defensive — the wallet + // shouldn't produce these, but we don't trust the wire). + const sigByAddr = new Map(); + for (const entry of signatures) { + if (!entry || typeof entry.address !== 'string' || typeof entry.signature !== 'string') { + return fail('malformed_signatures'); + } + const prev = sigByAddr.get(entry.address); + if (prev && prev !== entry.signature) return fail('duplicate_signatures'); + sigByAddr.set(entry.address, entry.signature); + } + + // 5) Every unique input address must be present in the signatures map + // AND its signature must verify. That's what "the claimant controls + // every funding source" cryptographically reduces to. + const inAddrs = getInputAddresses(tx); + if (inAddrs.size === 0) return fail('no_input_addresses'); + + for (const inAddr of inAddrs) { + const sig = sigByAddr.get(inAddr); + if (!sig) return fail(`missing_signature_for_input:${inAddr}`); + if (!verifyMessageSignature(requestId, sig, inAddr)) { + return fail(`bad_signature:${inAddr}`); + } + } + + // 6) Blocklist: payers caught double-spending earlier are refused. The + // canonical payer (first signature) is the one we blocklist on void, so we + // check that address here. + if (dedupStore && dedupStore.isBlocked(payerAddress)) { + return fail('blocklisted'); + } + + // 7) Zero-conf safety: refuse if the tx is currently voided or has a + // visible conflict. (Withheld double-spends are why we ALSO watch the tx + // post-serve; see voidWatcher.js.) + if (meta) { + if (Array.isArray(meta.voided_by) && meta.voided_by.length > 0) { + return fail('voided'); + } + if (Array.isArray(meta.conflict_with) && meta.conflict_with.length > 0) { + return fail('conflicted'); + } + } + + // 8) Tiered confirmation: amounts above the threshold must have first_block. + if (Number(requirements.amount) > zeroConfMaxAmount) { + if (!meta || !meta.first_block) return fail('awaiting_block'); + } + + // 9) Atomic dedup. This both detects replays and is the on-disk record of + // the redemption. We key dedup on (txId, outputIndex) and record the + // canonical payer (first signature address). + const dedup = dedupStore.markRedeemedIfFree( + { txId, outputIndex: paying.outputIndex }, + { + requestId, + payerAddress, + amount: requirements.amount, + asset: requirements.asset, + } + ); + + if (dedup.result === 'conflict') return fail('replay'); + const idempotent = dedup.result === 'idempotent'; + + return { + valid: true, + txId, + outputIndex: paying.outputIndex, + payerAddress, // first signature's address — used for refund target etc. + amount: String(paying.value), + paidAmount: String(paying.value), + asset: paying.token, + requestId, + idempotent, + existingRecord: idempotent ? dedup.existing : null, + }; +} + +module.exports = { verifyDirectPayment }; diff --git a/voidWatcher.js b/voidWatcher.js new file mode 100644 index 0000000..c6515cd --- /dev/null +++ b/voidWatcher.js @@ -0,0 +1,60 @@ +// Background polling watcher: after we serve a resource on a zero-conf payment, +// we keep an eye on the tx in case the payer double-spends. On a detected void +// we mark the dedup row voided and add the payer to the blocklist. +// +// Best-effort, in-process — lost on restart. POC tradeoff. Real production +// would use the fullnode event WebSocket (VERTEX_METADATA_CHANGED). + +'use strict'; + +const { getTransaction } = require('./helpers'); + +// Cumulative backoff. ~17 min total then we give up. +const SCHEDULE_MS = [5_000, 10_000, 20_000, 40_000, 80_000, 160_000, 320_000, 600_000]; + +function watchForVoid({ txId, outputIndex, payerAddress, store, log }) { + let cancelled = false; + let i = 0; + + const tick = async () => { + if (cancelled) return; + try { + const resp = await getTransaction(txId); + if (resp && resp.success && resp.meta) { + const meta = resp.meta; + const voided = Array.isArray(meta.voided_by) && meta.voided_by.length > 0; + const conflict = Array.isArray(meta.conflict_with) && meta.conflict_with.length > 0; + + if (voided) { + store.markVoided(txId, outputIndex); + if (payerAddress) store.addBlock(payerAddress, 'double_spend', txId); + if (log) log('VOID-WATCH', `${txId}:${outputIndex} voided; blocked ${payerAddress}`); + return; // stop + } + + if (conflict) { + // Conflict but our tx hasn't been voided yet — keep watching, but + // log it so we have a breadcrumb. + if (log) log('VOID-WATCH', `${txId}:${outputIndex} has unresolved conflict, continuing to watch`); + } + + if (meta.first_block) { + // Confirmed: the tx is reorg-protected. Stop watching. + if (log) log('VOID-WATCH', `${txId}:${outputIndex} confirmed by block ${meta.first_block}`); + return; + } + } + } catch (err) { + if (log) log('VOID-WATCH', `poll error for ${txId}: ${err.message}`); + } + + if (++i >= SCHEDULE_MS.length || cancelled) return; + setTimeout(tick, SCHEDULE_MS[i] - (SCHEDULE_MS[i - 1] || 0)); + }; + + setTimeout(tick, SCHEDULE_MS[0]); + + return { cancel: () => { cancelled = true; } }; +} + +module.exports = { watchForVoid };