diff --git a/src/app/dashboard/preclaim/page.tsx b/src/app/dashboard/preclaim/page.tsx index 57e45ef..1a3cd82 100644 --- a/src/app/dashboard/preclaim/page.tsx +++ b/src/app/dashboard/preclaim/page.tsx @@ -37,7 +37,7 @@ function Page() {
- 2. AFTER + 2. AFTER
void - onSubmit: (images: File[]) => void + onSubmit: (ipfsUri: string) => void + userAddress?: string +} + +interface UploadStatus { + status: 'idle' | 'uploading' | 'success' | 'error' + message: string + progress: number } const ImageUploadModal = ({ isOpen, onClose, onSubmit, + userAddress = '', }: ImageUploadModalProps) => { const [beforeImage, setBeforeImage] = useState(null) const [afterImage, setAfterImage] = useState(null) + const [cleanupDate, setCleanupDate] = useState('') + const [cleanupTime, setCleanupTime] = useState('') const [step, setStep] = useState(1) + const [uploadStatus, setUploadStatus] = useState({ + status: 'idle', + message: '', + progress: 0 + }) const { checkBox, setCheckBox, setCleanupPicture } = useCleanupContext() if (!isOpen) return null + // Validate image file types + const validateImageFile = (file: File): boolean => { + const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp'] + const maxSize = 10 * 1024 * 1024 // 10MB + + if (!validTypes.includes(file.type)) { + setUploadStatus({ + status: 'error', + message: 'Please upload valid image files (JPEG, PNG, WebP)', + progress: 0 + }) + return false + } + + if (file.size > maxSize) { + setUploadStatus({ + status: 'error', + message: 'Image files must be smaller than 10MB', + progress: 0 + }) + return false + } + + return true + } + + // Test Pinata connection + const testPinataConnection = async (): Promise => { + const PINATA_API_KEY = process.env.NEXT_PUBLIC_PINATA_API_KEY + const PINATA_SECRET_KEY = process.env.NEXT_PUBLIC_PINATA_SECRET_KEY + + if (!PINATA_API_KEY || !PINATA_SECRET_KEY) { + console.error('Pinata API keys not configured') + return false + } + + try { + const response = await fetch('https://api.pinata.cloud/data/testAuthentication', { + method: 'GET', + headers: { + 'pinata_api_key': PINATA_API_KEY, + 'pinata_secret_api_key': PINATA_SECRET_KEY, + }, + }) + + const result = await response.json() + console.log('Pinata connection test:', result) + return response.ok + } catch (error) { + console.error('Pinata connection failed:', error) + return false + } + } + + // Upload single file to Pinata + const uploadSingleFile = async (file: File, filename: string): Promise => { + const PINATA_API_KEY = process.env.NEXT_PUBLIC_PINATA_API_KEY + const PINATA_SECRET_KEY = process.env.NEXT_PUBLIC_PINATA_SECRET_KEY + + if (!PINATA_API_KEY || !PINATA_SECRET_KEY) { + throw new Error('Pinata API keys not configured') + } + + const formData = new FormData() + formData.append('file', file, filename) + + const pinataMetadata = { + name: filename, + keyvalues: { + userAddress, + type: 'cleanup-image', + timestamp: new Date().toISOString() + } + } + + formData.append('pinataMetadata', JSON.stringify(pinataMetadata)) + + const response = await fetch('https://api.pinata.cloud/pinning/pinFileToIPFS', { + method: 'POST', + headers: { + 'pinata_api_key': PINATA_API_KEY, + 'pinata_secret_api_key': PINATA_SECRET_KEY, + }, + body: formData, + }) + + const responseText = await response.text() + + if (!response.ok) { + console.error('Pinata response:', responseText) + throw new Error(`Pinata upload failed: ${response.status} ${response.statusText} - ${responseText}`) + } + + const result = JSON.parse(responseText) + return result.IpfsHash + } + + // Create and upload metadata JSON + const uploadMetadata = async (metadata: any): Promise => { + const PINATA_API_KEY = process.env.NEXT_PUBLIC_PINATA_API_KEY + const PINATA_SECRET_KEY = process.env.NEXT_PUBLIC_PINATA_SECRET_KEY + + if (!PINATA_API_KEY || !PINATA_SECRET_KEY) { + throw new Error('Pinata API keys not configured') + } + + const metadataBlob = new Blob([JSON.stringify(metadata, null, 2)], { + type: 'application/json' + }) + + const formData = new FormData() + formData.append('file', metadataBlob, 'cleanup-metadata.json') + + const pinataMetadata = { + name: 'Cleanup Submission Metadata', + keyvalues: { + userAddress: metadata.userAddress, + type: 'cleanup-metadata', + timestamp: metadata.submissionTimestamp + } + } + + formData.append('pinataMetadata', JSON.stringify(pinataMetadata)) + + const response = await fetch('https://api.pinata.cloud/pinning/pinFileToIPFS', { + method: 'POST', + headers: { + 'pinata_api_key': PINATA_API_KEY, + 'pinata_secret_api_key': PINATA_SECRET_KEY, + }, + body: formData, + }) + + const responseText = await response.text() + + if (!response.ok) { + console.error('Pinata metadata response:', responseText) + throw new Error(`Metadata upload failed: ${response.status} ${response.statusText}`) + } + + const result = JSON.parse(responseText) + return result.IpfsHash + } + const handleDragOver = (e: React.DragEvent) => { e.preventDefault() } - const handleSubmit = () => { - if (!checkBox) return - setStep(1) - // Store images in context - setCleanupPicture({ - before: beforeImage, - after: afterImage, + const handleSubmit = async () => { + // Validation + if (!checkBox) { + setUploadStatus({ + status: 'error', + message: 'Please accept the social consent checkbox', + progress: 0 + }) + return + } + + if (!beforeImage || !afterImage) { + setUploadStatus({ + status: 'error', + message: 'Please provide both before and after photos', + progress: 0 + }) + return + } + + if (!cleanupDate || !cleanupTime) { + setUploadStatus({ + status: 'error', + message: 'Please provide the cleanup date and time', + progress: 0 + }) + return + } + + // Validate image files + if (!validateImageFile(beforeImage) || !validateImageFile(afterImage)) { + return + } + + setUploadStatus({ + status: 'uploading', + message: 'Testing connection...', + progress: 5 }) - // Submit the images - const images = [] - if (beforeImage) images.push(beforeImage) - if (afterImage) images.push(afterImage) - onSubmit(images) + try { + // Test Pinata connection first + const connectionTest = await testPinataConnection() + if (!connectionTest) { + throw new Error('Failed to connect to Pinata. Please check your API keys.') + } - // Reset form but keep images in context for preview - setBeforeImage(null) - setAfterImage(null) - setCheckBox(false) + setUploadStatus({ + status: 'uploading', + message: 'Uploading before image...', + progress: 20 + }) + + // Upload before image + const beforeImageHash = await uploadSingleFile( + beforeImage, + `before_${Date.now()}.${beforeImage.name.split('.').pop()}` + ) + + setUploadStatus({ + status: 'uploading', + message: 'Uploading after image...', + progress: 50 + }) + + // Upload after image + const afterImageHash = await uploadSingleFile( + afterImage, + `after_${Date.now()}.${afterImage.name.split('.').pop()}` + ) + + setUploadStatus({ + status: 'uploading', + message: 'Creating metadata...', + progress: 80 + }) + + // Create comprehensive metadata + const submissionTimestamp = new Date().toISOString() + const cleanupDateTime = new Date(`${cleanupDate}T${cleanupTime}`).toISOString() + + const metadata = { + userAddress, + submissionTimestamp, + cleanupDateTime, + images: { + before: { + hash: beforeImageHash, + ipfsUri: `ipfs://${beforeImageHash}`, + name: beforeImage.name, + size: beforeImage.size, + type: beforeImage.type + }, + after: { + hash: afterImageHash, + ipfsUri: `ipfs://${afterImageHash}`, + name: afterImage.name, + size: afterImage.size, + type: afterImage.type + } + }, + version: '1.0', + platform: 'cleanup-app' + } + + // Upload metadata + const metadataHash = await uploadMetadata(metadata) + + setUploadStatus({ + status: 'success', + message: 'Upload successful!', + progress: 100 + }) + + // Store images in context + setCleanupPicture({ + before: beforeImage, + after: afterImage, + }) + + // Submit the metadata IPFS URI (which contains links to both images) + onSubmit(`ipfs://${metadataHash}`) + + // Reset form + setTimeout(() => { + handleClose() + }, 1500) + + } catch (error) { + console.error('Upload error:', error) + setUploadStatus({ + status: 'error', + message: `Upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`, + progress: 0 + }) + } } const handleClose = () => { setStep(1) setBeforeImage(null) setAfterImage(null) + setCleanupDate('') + setCleanupTime('') + setUploadStatus({ status: 'idle', message: '', progress: 0 }) onClose() } + const isFormValid = beforeImage && afterImage && cleanupDate && cleanupTime && checkBox + return (
-
+
{/* Close button */} {/* Header */}
-

Upload Image

+

Upload Cleanup Images

{/* Content */} @@ -76,43 +358,47 @@ const ImageUploadModal = ({
{/* Image uploaders */} -
+
{ + if (file && validateImageFile(file)) { + setBeforeImage(file) + setCleanupPicture(prev => ({ ...prev, before: file })) + } + }} + label='1. Snap a photo of the area before you start. Show the impact your cleanup will make!' onDragOver={handleDragOver} onDrop={e => { e.preventDefault() - if ( - e.dataTransfer.files && - e.dataTransfer.files.length > 0 - ) { - setBeforeImage(e.dataTransfer.files[0]) - setCleanupPicture(prev => ({ - ...prev, - before: e.dataTransfer.files[0], - })) + if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { + const file = e.dataTransfer.files[0] + if (validateImageFile(file)) { + setBeforeImage(file) + setCleanupPicture(prev => ({ ...prev, before: file })) + } } }} /> { + if (file && validateImageFile(file)) { + setAfterImage(file) + setCleanupPicture(prev => ({ ...prev, after: file })) + } + }} label='2. Capture the transformed space! Upload your after photo to complete your submission and earn rewards.' onDragOver={handleDragOver} onDrop={e => { e.preventDefault() - if ( - e.dataTransfer.files && - e.dataTransfer.files.length > 0 - ) { - setAfterImage(e.dataTransfer.files[0]) - setCleanupPicture(prev => ({ - ...prev, - after: e.dataTransfer.files[0], - })) + if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { + const file = e.dataTransfer.files[0] + if (validateImageFile(file)) { + setAfterImage(file) + setCleanupPicture(prev => ({ ...prev, after: file })) + } } }} /> @@ -125,57 +411,53 @@ const ImageUploadModal = ({
{step === 1 ? ( -
+
{ + if (file && validateImageFile(file)) { + setBeforeImage(file) + setCleanupPicture(prev => ({ ...prev, before: file })) + } + }} + label='1. Snap a photo of the area before you start. Show the impact your cleanup will make!' onDragOver={handleDragOver} onDrop={e => { e.preventDefault() - if ( - e.dataTransfer.files && - e.dataTransfer.files.length > 0 - ) { - setBeforeImage(e.dataTransfer.files[0]) - setCleanupPicture(prev => ({ - ...prev, - before: e.dataTransfer.files[0], - })) + if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { + const file = e.dataTransfer.files[0] + if (validateImageFile(file)) { + setBeforeImage(file) + setCleanupPicture(prev => ({ ...prev, before: file })) + } } }} />
) : ( -
+
{ + if (file && validateImageFile(file)) { + setAfterImage(file) + setCleanupPicture(prev => ({ ...prev, after: file })) + } + }} label='2. Capture the transformed space! Upload your after photo to complete your submission and earn rewards.' onDragOver={handleDragOver} onDrop={e => { e.preventDefault() - if ( - e.dataTransfer.files && - e.dataTransfer.files.length > 0 - ) { - setAfterImage(e.dataTransfer.files[0]) - setCleanupPicture(prev => ({ - ...prev, - after: e.dataTransfer.files[0], - })) + if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { + const file = e.dataTransfer.files[0] + if (validateImageFile(file)) { + setAfterImage(file) + setCleanupPicture(prev => ({ ...prev, after: file })) + } } }} /> @@ -184,36 +466,111 @@ const ImageUploadModal = ({
{/* Right sidebar */} -
-
+
+
+ {/* Date and Time Fields */} +
+

Cleanup Details

+ +
+ + setCleanupDate(e.target.value)} + className='w-full p-2 border border-gray-300 rounded bg-white text-black' + max={new Date().toISOString().split('T')[0]} + required + /> +
+ +
+ + setCleanupTime(e.target.value)} + className='w-full p-2 border border-gray-300 rounded bg-white text-black' + required + /> +
+
+ + {/* Upload Status */} + {uploadStatus.status !== 'idle' && ( +
+
+ {uploadStatus.status === 'uploading' && ( + + )} + {uploadStatus.status === 'success' && ( + + )} + {uploadStatus.status === 'error' && ( + + )} + + {uploadStatus.message} + +
+ + {uploadStatus.status === 'uploading' && ( +
+
+
+ )} +
+ )} +
@@ -225,4 +582,4 @@ const ImageUploadModal = ({ ) } -export default ImageUploadModal +export default ImageUploadModal \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 54a869b..df02c58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -300,6 +300,7 @@ ethereum-cryptography "^2.0.0" micro-ftch "^0.3.1" + "@ethersproject/abi@^5.8.0", "@ethersproject/abi@5.8.0": version "5.8.0" resolved "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.8.0.tgz" @@ -718,6 +719,11 @@ resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz" integrity sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q== +"@img/sharp-libvips-linuxmusl-x64@1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz" + integrity sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A== + "@img/sharp-linux-x64@0.34.1": version "0.34.1" resolved "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.1.tgz" @@ -725,6 +731,13 @@ optionalDependencies: "@img/sharp-libvips-linux-x64" "1.1.0" +"@img/sharp-linuxmusl-x64@0.34.1": + version "0.34.1" + resolved "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.1.tgz" + integrity sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-x64" "1.1.0" + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" @@ -1017,6 +1030,11 @@ resolved "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.1.tgz" integrity sha512-bfI4AMhySJbyXQIKH5rmLJ5/BP7bPwuxauTvVEiJ/ADoddaA9fgyNNCcsbu9SlqfHDoZmfI6g2EjzLwbsVTr5A== +"@next/swc-linux-x64-musl@15.3.1": + version "15.3.1" + resolved "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.1.tgz" + integrity sha512-FeAbR7FYMWR+Z+M5iSGytVryKHiAsc0x3Nc3J+FD5NVbD5Mqz7fTSy8CYliXinn7T26nDMbpExRUI/4ekTvoiA== + "@noble/ciphers@^1.0.0", "@noble/ciphers@1.2.1": version "1.2.1" resolved "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.2.1.tgz" @@ -1901,6 +1919,8 @@ integrity sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw== dependencies: undici-types "~6.21.0" + dependencies: + undici-types "~6.21.0" "@types/node@22.7.5": version "22.7.5" @@ -2054,6 +2074,11 @@ resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.7.2.tgz" integrity sha512-dEidzJDubxxhUCBJ/SHSMJD/9q7JkyfBMT77Px1npl4xpg9t0POLvnWywSk66BgZS/b2Hy9Y1yFaoMTFJUe9yg== +"@unrs/resolver-binding-linux-x64-musl@1.7.2": + version "1.7.2" + resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.7.2.tgz" + integrity sha512-RvP+Ux3wDjmnZDT4XWFfNBRVG0fMsc+yVzNFUqOflnDfZ9OYujv6nkh+GOr+watwrW4wdp6ASfG/e7bkDradsw== + "@vanilla-extract/css@^1.0.0", "@vanilla-extract/css@1.15.5": version "1.15.5" resolved "https://registry.npmjs.org/@vanilla-extract/css/-/css-1.15.5.tgz" @@ -2401,6 +2426,10 @@ "@walletconnect/logger" "2.1.2" events "3.3.0" +"@walletconnect/types@2.20.0": + version "2.20.0" + resolved "https://registry.npmjs.org/@walletconnect/types/-/types-2.20.0.tgz" + integrity sha512-oFGHRL/yQbZqBiTA8yvV+PGJYBU/laDAQWFiJZ9Xlv+qN5EzHipW39Ru6qyp8P4DGnbQI6bHPs9bizJ7hkDRKA== "@walletconnect/types@2.20.1": version "2.20.1" resolved "https://registry.npmjs.org/@walletconnect/types/-/types-2.20.1.tgz" @@ -2413,18 +2442,21 @@ "@walletconnect/logger" "2.1.2" events "3.3.0" +"@walletconnect/types@2.20.1": + version "2.20.1" + resolved "https://registry.npmjs.org/@walletconnect/types/-/types-2.20.1.tgz" + integrity sha512-HM0YZxT+wNqskoZkuju5owbKTlqUXNKfGlJk/zh9pWaVWBR2QamvQ+47Cx09OoGPRQjQH0JmgRiUV4bOwWNeHg== "@walletconnect/universal-provider@2.19.2": version "2.19.2" resolved "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.19.2.tgz" integrity sha512-LkKg+EjcSUpPUhhvRANgkjPL38wJPIWumAYD8OK/g4OFuJ4W3lS/XTCKthABQfFqmiNbNbVllmywiyE44KdpQg== dependencies: "@walletconnect/events" "1.0.1" - "@walletconnect/jsonrpc-http-connection" "1.0.8" - "@walletconnect/jsonrpc-provider" "1.0.14" + "@walletconnect/heartbeat" "1.2.2" "@walletconnect/jsonrpc-types" "1.0.4" - "@walletconnect/jsonrpc-utils" "1.0.8" "@walletconnect/keyvaluestorage" "1.1.1" "@walletconnect/logger" "2.1.2" + "@walletconnect/sign-client" "2.19.2" "@walletconnect/types" "2.19.2" "@walletconnect/utils" "2.19.2" @@ -2490,6 +2522,65 @@ uint8arrays "3.1.0" viem "2.23.2" +"@walletconnect/universal-provider@2.20.0": + version "2.20.0" + resolved "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.20.0.tgz" + integrity sha512-kzMWXao+RyWfv46nS/owJ99/QhObGkYHhpMxdzl4bae98JXdQ0xhmov3Rvy3GRt5csgJXldoM2VO44B/Fsuj4Q== + dependencies: + "@walletconnect/events" "1.0.1" + "@walletconnect/jsonrpc-http-connection" "1.0.8" + "@walletconnect/jsonrpc-provider" "1.0.14" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/keyvaluestorage" "1.1.1" + "@walletconnect/logger" "2.1.2" + "@walletconnect/sign-client" "2.20.0" + "@walletconnect/types" "2.20.0" + "@walletconnect/utils" "2.20.0" + es-toolkit "1.33.0" + events "3.3.0" + +"@walletconnect/universal-provider@2.20.1": + version "2.20.1" + resolved "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.20.1.tgz" + integrity sha512-0WfO4Unb+8UMEUr65vrVjd/a/3tF5059KLP7gX2kaDFjXXOma1/cjq/9/STd3hbHB8LKfxpXvOty97vuD8xfWQ== + dependencies: + "@walletconnect/events" "1.0.1" + "@walletconnect/jsonrpc-http-connection" "1.0.8" + "@walletconnect/jsonrpc-provider" "1.0.14" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/keyvaluestorage" "1.1.1" + "@walletconnect/logger" "2.1.2" + "@walletconnect/sign-client" "2.20.1" + "@walletconnect/types" "2.20.1" + "@walletconnect/utils" "2.20.1" + es-toolkit "1.33.0" + events "3.3.0" + +"@walletconnect/utils@2.19.2": + version "2.19.2" + resolved "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.19.2.tgz" + integrity sha512-VU5CcUF4sZDg8a2/ov29OJzT3KfLuZqJUM0GemW30dlipI5fkpb0VPenZK7TcdLPXc1LN+Q+7eyTqHRoAu/BIA== + dependencies: + "@noble/ciphers" "1.2.1" + "@noble/curves" "1.8.1" + "@noble/hashes" "1.7.1" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/keyvaluestorage" "1.1.1" + "@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.19.2" + "@walletconnect/window-getters" "1.0.1" + "@walletconnect/window-metadata" "1.0.1" + bs58 "6.0.0" + detect-browser "5.3.0" + query-string "7.1.3" + uint8arrays "3.1.0" + viem "2.23.2" + "@walletconnect/utils@2.20.0": version "2.20.0" resolved "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.20.0.tgz" @@ -2888,11 +2979,6 @@ binary-extensions@^2.0.0: resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== -bn.js@^4.11.9: - version "4.12.2" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz" - integrity sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw== - bn.js@^5.2.1: version "5.2.2" resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz" @@ -4806,6 +4892,7 @@ gopd@^1.0.1, gopd@^1.2.0: resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "4.2.11" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" @@ -4959,6 +5046,7 @@ hast-util-whitespace@^3.0.0: dependencies: "@types/hast" "^3.0.0" + hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz" @@ -5043,7 +5131,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@2: +inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@2: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -6296,16 +6384,6 @@ mimic-function@^5.0.0: resolved "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz" integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" - integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== - minimatch@^10.0.0: version "10.0.1" resolved "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz" @@ -8303,6 +8381,9 @@ synckit@^0.11.0: dependencies: "@pkgr/core" "^0.2.3" tslib "^2.8.1" + dependencies: + "@pkgr/core" "^0.2.3" + tslib "^2.8.1" table-layout@^1.0.2: version "1.0.2" @@ -8595,6 +8676,10 @@ typedarray@^0.0.6: resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== +typescript@^5.5.3, typescript@^5.7.2, typescript@>=3.3.1, typescript@>=4.2.0, typescript@>=5.0.4, typescript@>=5.4.0: + version "5.8.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz" + integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== typescript@^5.5.3, typescript@^5.7.2, typescript@>=3.3.1, typescript@>=3.7.0, typescript@>=4.2.0, typescript@>=4.3.0, typescript@>=5.0.4, typescript@>=5.4.0: version "5.8.3" resolved "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz" @@ -8872,16 +8957,16 @@ use-sidecar@^1.1.2, use-sidecar@^1.1.3: detect-node-es "^1.1.0" tslib "^2.0.0" -use-sync-external-store@>=1.2.0, use-sync-external-store@1.2.0: - 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== - -use-sync-external-store@1.4.0: +use-sync-external-store@>=1.2.0, use-sync-external-store@1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz" integrity sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw== +use-sync-external-store@1.2.0: + 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== + utf-8-validate@^5.0.2, utf-8-validate@>=5.0.2: version "5.0.10" resolved "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz" @@ -9196,6 +9281,7 @@ wrappy@1: resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +ws@*, ws@^7.5.1: ws@*, ws@~8.17.1, ws@8.17.1: version "8.17.1" resolved "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz" @@ -9206,6 +9292,12 @@ ws@^7.5.1: resolved "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== +ws@~8.17.1: + version "8.17.1" + resolved "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== + + ws@8.18.0: version "8.18.0" resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz"