diff --git a/frontend/wallet/app/receive/page.tsx b/frontend/wallet/app/receive/page.tsx index 07cb61a..8645bf1 100644 --- a/frontend/wallet/app/receive/page.tsx +++ b/frontend/wallet/app/receive/page.tsx @@ -1,13 +1,15 @@ 'use client' -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { useRouter } from 'next/navigation' -import { QRCodeSVG } from 'qrcode.react' +import { QRCodeCanvas } from 'qrcode.react' export default function ReceivePage() { const router = useRouter() const [walletAddress, setWalletAddress] = useState(null) const [copied, setCopied] = useState(false) + const [downloading, setDownloading] = useState(false) + const qrRef = useRef(null) useEffect(() => { const stored = sessionStorage.getItem('invisible_wallet_address') @@ -15,6 +17,7 @@ export default function ReceivePage() { setWalletAddress(stored) }, [router]) + // ── Copy address ──────────────────────────────────────────────────────────── const handleCopy = async () => { if (!walletAddress) return await navigator.clipboard.writeText(walletAddress) @@ -22,6 +25,60 @@ export default function ReceivePage() { setTimeout(() => setCopied(false), 2000) } + // ── Download QR as PNG ────────────────────────────────────────────────────── + // QRCodeCanvas renders a element — we grab it directly and trigger + // a download without any server round-trip. + const handleDownload = () => { + if (!walletAddress || !qrRef.current) return + setDownloading(true) + + const canvas = qrRef.current.querySelector('canvas') + if (!canvas) { setDownloading(false); return } + + // Paint a white-padded version so the PNG looks nice when shared + const pad = 24 + const out = document.createElement('canvas') + out.width = canvas.width + pad * 2 + out.height = canvas.height + pad * 2 + const ctx = out.getContext('2d')! + ctx.fillStyle = '#ffffff' + ctx.fillRect(0, 0, out.width, out.height) + ctx.drawImage(canvas, pad, pad) + + const link = document.createElement('a') + link.download = `veil-address-${walletAddress.slice(0, 8)}.png` + link.href = out.toDataURL('image/png') + link.click() + + setDownloading(false) + } + + // ── Web Share API ─────────────────────────────────────────────────────────── + // On mobile browsers (Chrome Android, Safari iOS) this opens the native + // share sheet. On desktop it falls back gracefully to copy. + const handleShare = async () => { + if (!walletAddress) return + + if (navigator.share) { + try { + await navigator.share({ + title: 'My Veil Wallet Address', + text: walletAddress, + }) + } catch { + // User dismissed the share sheet — ignore + } + return + } + + // Desktop fallback: copy to clipboard + await navigator.clipboard.writeText(walletAddress) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } + + const canShare = typeof navigator !== 'undefined' && !!navigator.share + return (
@@ -59,15 +116,18 @@ export default function ReceivePage() { {walletAddress ? ( <> - {/* QR code */} -
- so we can export to PNG */} +
+ {/* Address display */} -
+

WALLET ADDRESS

@@ -92,29 +152,69 @@ export default function ReceivePage() {

- {/* Copy button */} - + {/* Action buttons */} +
+ {/* Copy address */} + + + {/* Download QR as PNG */} + + + {/* Share — shows native sheet on mobile, copies on desktop */} + + +
) : (
@@ -123,4 +223,4 @@ export default function ReceivePage() {
) -} +} \ No newline at end of file diff --git a/frontend/wallet/app/send/page.tsx b/frontend/wallet/app/send/page.tsx index 1bcd618..2bfba74 100644 --- a/frontend/wallet/app/send/page.tsx +++ b/frontend/wallet/app/send/page.tsx @@ -1,6 +1,6 @@ 'use client' -import { useState, useEffect } from 'react' +import { useState, useEffect, useRef } from 'react' import { useRouter } from 'next/navigation' import { Keypair, Networks, TransactionBuilder, BASE_FEE, Asset, Operation, @@ -33,6 +33,10 @@ export default function SendPage() { const [showPicker, setShowPicker] = useState(false) const [showScanner, setShowScanner] = useState(false) const [hasCamera, setHasCamera] = useState(false) + const [imgError, setImgError] = useState(null) + const [imgDecoding, setImgDecoding] = useState(false) + + const fileInputRef = useRef(null) const [assets, setAssets] = useState([]) const [selectedAsset, setSelectedAsset] = useState(null) @@ -41,18 +45,14 @@ export default function SendPage() { const addr = sessionStorage.getItem('invisible_wallet_address') if (!addr) { router.replace('/lock'); return } - // Check camera/QR scanner availability before showing scan button if (typeof (window as unknown as { BarcodeDetector?: unknown }).BarcodeDetector !== 'undefined' || !!navigator.mediaDevices?.getUserMedia) { setHasCamera(true) } - // Fetch available balances — use the fee-payer G... account since - // Horizon doesn't support loadAccount for C... contract addresses. const signerPublicKey = sessionStorage.getItem('veil_signer_secret') ? Keypair.fromSecret(sessionStorage.getItem('veil_signer_secret')!).publicKey() : localStorage.getItem('veil_signer_public_key') || null if (!signerPublicKey || !signerPublicKey.startsWith('G')) { - // No fee-payer G... key available yet — default to XLM const xlm: WalletAsset = { code: 'XLM', issuer: null, contractId: Asset.native().contractId(Networks.TESTNET) } setAssets([xlm]) setSelectedAsset(xlm) @@ -71,15 +71,68 @@ export default function SendPage() { setAssets(list) if (list.length > 0) setSelectedAsset(list[0]) }).catch(() => { - // Account not yet funded — default to XLM const xlm: WalletAsset = { code: 'XLM', issuer: null, contractId: Asset.native().contractId(Networks.TESTNET) } setAssets([xlm]) setSelectedAsset(xlm) }) }, [router]) + // ── QR image upload ───────────────────────────────────────────────────────── + // Reads an image file, draws it to an offscreen canvas, and passes the + // ImageData to BarcodeDetector. Falls back to a clear error if the browser + // doesn't support BarcodeDetector or no QR is found in the image. + const handleImageFile = async (file: File) => { + setImgError(null) + setImgDecoding(true) + + try { + // Decode image into a bitmap + const bitmap = await createImageBitmap(file) + + // Draw onto an offscreen canvas so BarcodeDetector can read it + const canvas = document.createElement('canvas') + canvas.width = bitmap.width + canvas.height = bitmap.height + const ctx = canvas.getContext('2d')! + ctx.drawImage(bitmap, 0, 0) + bitmap.close() + + const BarcodeDetectorClass = ( + window as unknown as { BarcodeDetector?: new (opts: { formats: string[] }) => { detect: (src: HTMLCanvasElement) => Promise<{ rawValue: string }[]> } } + ).BarcodeDetector + + if (!BarcodeDetectorClass) { + setImgError('QR image scan is not supported in this browser. Please type the address manually or use the camera scanner.') + return + } + + const detector = new BarcodeDetectorClass({ formats: ['qr_code'] }) + const codes = await detector.detect(canvas) + + if (codes.length === 0) { + setImgError('No QR code found in the image. Try a clearer photo.') + return + } + + const value = codes[0].rawValue.trim() + const isAddress = (value.startsWith('G') || value.startsWith('C')) && value.length === 56 + if (!isAddress) { + setImgError(`QR decoded "${value.slice(0, 20)}…" — doesn't look like a Stellar address.`) + return + } + + setRecipient(value) + setImgError(null) + } catch { + setImgError('Could not read the image. Please try a different file.') + } finally { + setImgDecoding(false) + // Reset file input so the same file can be re-selected if needed + if (fileInputRef.current) fileInputRef.current.value = '' + } + } + function validateForm(): boolean { - // Accept both classic G... accounts and C... Soroban contract addresses const validAddress = (recipient.startsWith('G') || recipient.startsWith('C')) && recipient.length === 56 if (!validAddress) return false if (isNaN(parseFloat(amount)) || parseFloat(amount) <= 0) return false @@ -101,7 +154,6 @@ export default function SendPage() { } const feePayerKp = Keypair.fromSecret(signerSecret) - // ── Step 1: Passkey verification (user must biometrically confirm) ────── const keyId = localStorage.getItem('invisible_wallet_key_id') if (!keyId) throw new Error('No passkey found. Please register the wallet first.') const credIdBin = atob(keyId.replace(/-/g, '+').replace(/_/g, '/')) @@ -116,13 +168,9 @@ export default function SendPage() { }) if (!assertion) throw new Error('Passkey verification was cancelled.') - // ── Step 2: Submit payment from fee-payer (which holds the XLM) ───────── - // The wallet contract (C...) is the auth layer; the fee-payer G... account - // is where funds live. We send from the fee-payer directly. const horizonServer = new Server('https://horizon-testnet.stellar.org') if (recipient.startsWith('G') && recipient.length === 56) { - // Classic account → classic payment operation (fast, cheap) const account = await horizonServer.loadAccount(feePayerKp.publicKey()) const tx = new TransactionBuilder(account, { fee: BASE_FEE, @@ -139,7 +187,6 @@ export default function SendPage() { const result = await horizonServer.submitTransaction(tx) setTxHash(result.hash) } else { - // Contract address → SAC transfer from fee-payer via Soroban RPC const rpcServer = new SorobanRpc.Server('https://soroban-testnet.stellar.org') const feePayerAcct = await rpcServer.getAccount(feePayerKp.publicKey()) const sacContract = new Contract(Asset.native().contractId(Networks.TESTNET)) @@ -198,7 +245,6 @@ export default function SendPage() { return (
- {/* Nav */}
+
setRecipient(e.target.value.trim())} + onChange={e => { setRecipient(e.target.value.trim()); setImgError(null) }} autoComplete="off" spellCheck={false} style={{ flex: 1 }} /> + + {/* Camera QR scan */} {hasCamera && ( )} + + {/* Upload QR image */} + + + {/* Hidden file input — accepts images only */} + { + const file = e.target.files?.[0] + if (file) handleImageFile(file) + }} + />
+ + {/* Inline error for image decode failures */} + {imgError && ( +

+ {imgError} +

+ )}
@@ -428,6 +508,21 @@ export default function SendPage() { ) } +// Shared style for the small icon buttons next to the address field +const iconBtnStyle: React.CSSProperties = { + background: 'var(--surface-md)', + border: '1px solid var(--border-dim)', + borderRadius: '0.5rem', + cursor: 'pointer', + color: 'var(--off-white)', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: 44, + flexShrink: 0, + transition: 'opacity 0.15s', +} + function Row({ label, value, mono }: { label: string; value: string; mono?: boolean }) { return (
@@ -442,4 +537,4 @@ function Row({ label, value, mono }: { label: string; value: string; mono?: bool
) -} +} \ No newline at end of file diff --git a/frontend/wallet/package-lock.json b/frontend/wallet/package-lock.json index ebf2141..4be4ba0 100644 --- a/frontend/wallet/package-lock.json +++ b/frontend/wallet/package-lock.json @@ -82,7 +82,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1923,6 +1922,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -1933,6 +1933,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "license": "MIT", + "peer": true, "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -1942,7 +1943,8 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/glob": { "version": "7.2.0", @@ -1988,7 +1990,6 @@ "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -2024,6 +2025,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -2033,25 +2035,29 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -2062,13 +2068,15 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -2081,6 +2089,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "license": "MIT", + "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -2090,6 +2099,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -2098,13 +2108,15 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -2121,6 +2133,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -2134,6 +2147,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -2146,6 +2160,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -2160,6 +2175,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" @@ -2169,20 +2185,21 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": 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==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2195,6 +2212,7 @@ "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.13.0" }, @@ -2207,7 +2225,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2620,7 +2637,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2808,6 +2824,7 @@ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.0" } @@ -3183,6 +3200,7 @@ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", "license": "MIT", + "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" @@ -3281,7 +3299,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/es-object-atoms": { "version": "1.1.1", @@ -3341,6 +3360,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -3354,6 +3374,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -3366,6 +3387,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=4.0" } @@ -3375,6 +3397,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=4.0" } @@ -3399,6 +3422,7 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.8.x" } @@ -3839,7 +3863,8 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "peer": true }, "node_modules/globalthis": { "version": "1.0.4", @@ -4556,7 +4581,6 @@ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, "license": "MIT", - "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -4583,7 +4607,8 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -4658,6 +4683,7 @@ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.11.5" }, @@ -4889,7 +4915,8 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/next": { "version": "14.2.29", @@ -5273,7 +5300,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -5487,7 +5513,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -5500,7 +5525,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -5684,7 +5708,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz", "integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==", "license": "MIT", - "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -6308,6 +6331,7 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" }, @@ -6399,7 +6423,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -6518,7 +6541,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -6832,6 +6854,7 @@ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "license": "MIT", + "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -6900,6 +6923,7 @@ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.13.0" } @@ -6926,6 +6950,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -6937,13 +6962,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/webpack/node_modules/schema-utils": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "license": "MIT", + "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -7142,7 +7169,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1",