diff --git a/electron/ipcHandlers.ts b/electron/ipcHandlers.ts index 6a8b9e3..517c77c 100644 --- a/electron/ipcHandlers.ts +++ b/electron/ipcHandlers.ts @@ -6,32 +6,44 @@ import logger from './logger' const neptuneNative = require('@neptune/native') -// Create keystore into default wallets directory +const WALLETS_DIR = path.resolve(app.getPath('userData'), 'wallets') +fs.mkdirSync(WALLETS_DIR, { recursive: true }) + +function assertBasename(name: string) { + if (!name || name !== path.basename(name)) { + throw new Error('Invalid file name') + } +} + +function safeResolvePath(fileName: string) { + assertBasename(fileName) + const candidate = path.join(WALLETS_DIR, fileName) + const realBase = fs.realpathSync(WALLETS_DIR) + const realCandidate = fs.existsSync(candidate) ? fs.realpathSync(candidate) : candidate + if (!(realCandidate === realBase || realCandidate.startsWith(realBase + path.sep))) { + throw new Error('Access denied') + } + return realCandidate +} + ipcMain.handle('wallet:createKeystore', async (_event, seed, password) => { try { const keystore = await encrypt(seed, password) - - const savePath = path.join(process.cwd(), 'wallets') - fs.mkdirSync(savePath, { recursive: true }) - const timestamp = Date.now() const fileName = `neptune-wallet-${timestamp}.json` - const filePath = path.join(savePath, fileName) - fs.writeFileSync(filePath, keystore) - - return { filePath } + const filePath = path.join(WALLETS_DIR, fileName) + fs.writeFileSync(filePath, keystore, 'utf-8') + return { success: true, fileName } } catch (error: any) { logger.error('Error creating keystore:', error.message) throw error } }) -// New handler: let user choose folder and filename to save keystore -ipcMain.handle('wallet:saveKeystoreAs', async (_event, seed: string, password: string) => { +ipcMain.handle('wallet:saveKeystoreAs', async (_event, fileName: string) => { try { - const keystore = await encrypt(seed, password) - - // Use timestamp for default filename + const pathToFileName = safeResolvePath(fileName) + const keystore = fs.readFileSync(pathToFileName, 'utf-8') const timestamp = Date.now() const defaultName = `neptune-wallet-${timestamp}.json` const { canceled, filePath } = await dialog.showSaveDialog({ @@ -40,23 +52,25 @@ ipcMain.handle('wallet:saveKeystoreAs', async (_event, seed: string, password: s filters: [{ name: 'JSON', extensions: ['json'] }], }) - if (canceled || !filePath) return { filePath: null } + if (canceled || !filePath) return { success: false, filePath: null } + if (path.extname(filePath).toLowerCase() !== '.json') { + throw new Error('Invalid file extension. Please save as a .json file.') + } fs.mkdirSync(path.dirname(filePath), { recursive: true }) - fs.writeFileSync(filePath, keystore) - - return { filePath } + fs.writeFileSync(filePath, keystore, 'utf-8') + return { success: true, filePath } } catch (error: any) { logger.error('Error saving keystore (Save As):', error.message) throw error } }) -ipcMain.handle('wallet:decryptKeystore', async (_event, filePath, password) => { +ipcMain.handle('wallet:decryptKeystore', async (_event, fileName: string, password: string) => { try { + const filePath = safeResolvePath(fileName) const json = fs.readFileSync(filePath, 'utf-8') const phrase = await fromEncryptedJson(json, password) - return { phrase } } catch (error: any) { logger.error('Error decrypting keystore ipc:', error.message) @@ -66,69 +80,49 @@ ipcMain.handle('wallet:decryptKeystore', async (_event, filePath, password) => { ipcMain.handle('wallet:checkKeystore', async () => { try { - const walletDir = path.join(process.cwd(), 'wallets') - if (!fs.existsSync(walletDir)) return { exists: false, filePath: null } - + if (!fs.existsSync(WALLETS_DIR)) return { exists: false, fileName: null } const newestFile = fs - .readdirSync(walletDir) + .readdirSync(WALLETS_DIR) .filter((f) => f.endsWith('.json')) .sort( (a, b) => - fs.statSync(path.join(walletDir, b)).mtime.getTime() - - fs.statSync(path.join(walletDir, a)).mtime.getTime() + fs.statSync(path.join(WALLETS_DIR, b)).mtime.getTime() - + fs.statSync(path.join(WALLETS_DIR, a)).mtime.getTime() )[0] - if (!newestFile) return { exists: false, filePath: null } - - const resolvedPath = path.join(walletDir, newestFile) + if (!newestFile) return { exists: false, fileName: null } + const resolvedPath = safeResolvePath(newestFile) let minBlockHeight: number | null = null - try { const json = fs.readFileSync(resolvedPath, 'utf-8') const data = JSON.parse(json) const height = data?.minBlockHeight - - if (typeof height === 'number' && Number.isFinite(height)) { - minBlockHeight = height - } + if (Number.isFinite(height)) minBlockHeight = height } catch (error: any) { logger.warn('Unable to read minBlockHeight from keystore:', error.message) } - - return { exists: true, filePath: resolvedPath, minBlockHeight } + return { exists: true, fileName: newestFile, minBlockHeight } } catch (error: any) { logger.error('Error checking keystore ipc:', error.message) - return { exists: false, filePath: null, minBlockHeight: null, error: error.message } + return { exists: false, fileName: null, minBlockHeight: null, error: error.message } } }) ipcMain.handle( 'wallet:updateMinBlockHeight', - async (_event, filePath: string | null, minBlockHeight: number | null) => { - if (!filePath) { - return { success: false, error: 'No keystore file path provided.' } + async (_event, fileName: string | null, minBlockHeight: number | null) => { + if (!fileName) { + return { success: false, error: 'No keystore file name provided.' } } - try { - const normalizedPath = path.isAbsolute(filePath) - ? filePath - : path.join(process.cwd(), filePath) - - if (!fs.existsSync(normalizedPath)) { + const filePath = safeResolvePath(fileName) + if (!fs.existsSync(filePath)) { return { success: false, error: 'Keystore file not found.' } } - - const fileContents = fs.readFileSync(normalizedPath, 'utf-8') + const fileContents = fs.readFileSync(filePath, 'utf-8') const walletJson = JSON.parse(fileContents) - - if (minBlockHeight === null || Number.isNaN(minBlockHeight)) { - walletJson.minBlockHeight = null - } else { - walletJson.minBlockHeight = minBlockHeight - } - - fs.writeFileSync(normalizedPath, JSON.stringify(walletJson, null, 2), 'utf-8') - + walletJson.minBlockHeight = Number.isFinite(minBlockHeight) ? minBlockHeight : null + fs.writeFileSync(filePath, JSON.stringify(walletJson, null, 2), 'utf-8') return { success: true, minBlockHeight } } catch (error: any) { logger.error('Error updating min block height:', error.message) @@ -137,42 +131,32 @@ ipcMain.handle( } ) -ipcMain.handle( - 'wallet:getMinBlockHeight', - async (_event, filePath: string | null) => { - if (!filePath) { - return { success: false, error: 'No keystore file path provided.', minBlockHeight: null } - } - - try { - const normalizedPath = path.isAbsolute(filePath) - ? filePath - : path.join(process.cwd(), filePath) - - if (!fs.existsSync(normalizedPath)) { - return { success: false, error: 'Keystore file not found.', minBlockHeight: null } - } - - const fileContents = fs.readFileSync(normalizedPath, 'utf-8') - const walletJson = JSON.parse(fileContents) - const height = walletJson?.minBlockHeight - - if (typeof height === 'number' && Number.isFinite(height)) { - return { success: true, minBlockHeight: height } - } - - return { success: true, minBlockHeight: null } - } catch (error: any) { - logger.error('Error reading min block height:', error.message) - return { success: false, error: error.message, minBlockHeight: null } - } +ipcMain.handle('wallet:getMinBlockHeight', async (_event, fileName: string | null) => { + if (!fileName) { + return { success: false, error: 'No keystore file name provided.', minBlockHeight: null } } -) + try { + const filePath = safeResolvePath(fileName) + if (!fs.existsSync(filePath)) { + return { success: false, error: 'Keystore file not found.', minBlockHeight: null } + } + const fileContents = fs.readFileSync(filePath, 'utf-8') + const walletJson = JSON.parse(fileContents) + const height = walletJson?.minBlockHeight + return { + success: true, + minBlockHeight: Number.isFinite(height) ? height : null, + } + } catch (error: any) { + logger.error('Error reading min block height:', error.message) + return { success: false, error: error.message, minBlockHeight: null } + } +}) ipcMain.handle('wallet:generateKeysFromSeed', async (_event, seedPhrase: string[]) => { try { const wallet = new neptuneNative.WalletManager() - return wallet.generateKeysFromSeed(seedPhrase) + return JSON.parse(wallet.generateKeysFromSeed(seedPhrase)) } catch (error: any) { logger.error('Error generating keys from seed ipc:', error.message) throw error @@ -181,8 +165,36 @@ ipcMain.handle('wallet:generateKeysFromSeed', async (_event, seedPhrase: string[ ipcMain.handle('wallet:buildTransaction', async (_event, args) => { const { spendingKeyHex, inputAdditionRecords, outputAddresses, outputAmounts, fee } = args - try { + if (typeof spendingKeyHex !== 'string' || !/^(0x)?[0-9a-fA-F]+$/.test(spendingKeyHex)) { + throw new Error('Invalid spending key') + } + + if ( + !Array.isArray(inputAdditionRecords) || + !inputAdditionRecords.every((r) => typeof r === 'string') + ) { + throw new Error('Invalid inputAdditionRecords') + } + + if ( + !Array.isArray(outputAddresses) || + !outputAddresses.every((a) => typeof a === 'string') + ) { + throw new Error('Invalid outputAddresses') + } + + if ( + !Array.isArray(outputAmounts) || + !outputAmounts.every((a) => typeof a === 'string' && /^\d+(\.\d+)?$/.test(a)) + ) { + throw new Error('Invalid outputAmounts') + } + + if (typeof fee !== 'string' || !/^\d+(\.\d+)?$/.test(fee)) { + throw new Error('Invalid fee') + } + const builder = new neptuneNative.SimpleTransactionBuilder() const result = await builder.buildTransaction( import.meta.env.VITE_APP_API, @@ -197,11 +209,11 @@ ipcMain.handle('wallet:buildTransaction', async (_event, args) => { ) return JSON.parse(result) } catch (error: any) { - logger.error('Error building transaction with primitive proof ipc:', error.message) + logger.error('Error building transaction ipc:', error.message) throw error } }) -ipcMain.on('log:info', (_, msg: string) => logger.info(msg)) -ipcMain.on('log:warn', (_, msg: string) => logger.warn(msg)) -ipcMain.on('log:error', (_, msg: string) => logger.error(msg)) +ipcMain.on('log:info', (_, ...msg: string[]) => logger.info(...msg)) +ipcMain.on('log:warn', (_, ...msg: string[]) => logger.warn(...msg)) +ipcMain.on('log:error', (_, ...msg: string[]) => logger.error(...msg)) diff --git a/electron/logger.ts b/electron/logger.ts index e8298af..b9c4bb5 100644 --- a/electron/logger.ts +++ b/electron/logger.ts @@ -3,8 +3,6 @@ import log from 'electron-log' const isDev = app.isPackaged -console.log('isDev :>> ', isDev); - if (!isDev) { log.transports.file.level = 'info' log.transports.console.level = false @@ -12,21 +10,17 @@ if (!isDev) { } export const logger = { - info: (...args: any[]) => { - if (isDev) console.log('[INFO]', ...args) - else log.info(...args) + info: (...args: string[]) => { + if (!isDev) log.info(...args) }, - warn: (...args: any[]) => { - if (isDev) console.warn('[WARN]', ...args) - else log.warn(...args) + warn: (...args: string[]) => { + if (!isDev) log.warn(...args) }, - error: (...args: any[]) => { - if (isDev) console.error('[ERROR]', ...args) - else log.error(...args) + error: (...args: string[]) => { + if (!isDev) log.error(...args) }, - debug: (...args: any[]) => { - if (isDev) console.debug('[DEBUG]', ...args) - else log.debug(...args) + debug: (...args: string[]) => { + if (!isDev) log.debug(...args) }, } diff --git a/electron/main.ts b/electron/main.ts index 1ece828..942458e 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -4,10 +4,25 @@ import started from 'electron-squirrel-startup' import './ipcHandlers' import logger from './logger' +const isDev = !app.isPackaged + if (started) { app.quit() } +// Security: Handle certificate errors - reject invalid certificates to prevent phishing +app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { + if (isDev && (url.startsWith('http://localhost') || url.startsWith('http://127.0.0.1'))) { + event.preventDefault() + callback(true) + return + } + + logger.warn(`Certificate error for ${url}: ${error}`) + event.preventDefault() + callback(false) +}) + const createWindow = () => { logger.info('App started') const mainWindow = new BrowserWindow({ @@ -20,6 +35,8 @@ const createWindow = () => { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false, + devTools: isDev, + sandbox: true, }, }) if (MAIN_WINDOW_VITE_DEV_SERVER_URL) { @@ -28,7 +45,58 @@ const createWindow = () => { mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`)) } - mainWindow.webContents.openDevTools() + // Security: Prevent opening new windows - block all window.open() calls + mainWindow.webContents.setWindowOpenHandler(() => { + logger.warn('Blocked attempt to open new window') + return { action: 'deny' } + }) + + // Security: Control file downloads - validate and log all downloads + mainWindow.webContents.session.on('will-download', (event, item) => { + const fileName = item.getFilename() + const totalBytes = item.getTotalBytes() + + logger.info(`Download started: ${fileName} (${totalBytes} bytes)`) + + // Validate file extension - only allow safe file types + const allowedExtensions = ['.json', '.txt', '.csv'] + const fileExt = path.extname(fileName).toLowerCase() + + if (!allowedExtensions.includes(fileExt)) { + logger.warn(`Blocked download of potentially unsafe file: ${fileName}`) + item.cancel() + return + } + + // Log download progress + item.on('updated', () => { + logger.info( + `Download progress: ${fileName} - ${item.getReceivedBytes()}/${totalBytes} bytes` + ) + }) + + item.on('done', (event, state) => { + if (state === 'completed') { + logger.info(`Download completed: ${fileName}`) + } else if (state === 'cancelled') { + logger.warn(`Download cancelled: ${fileName}`) + } else { + logger.error(`Download interrupted: ${fileName} - ${state}`) + } + }) + }) + + if (!isDev) { + mainWindow.removeMenu() + mainWindow.webContents.on('before-input-event', (event, input) => { + if (input.control && input.shift && input.key.toLowerCase() === 'i') { + event.preventDefault() + } + }) + } + mainWindow.webContents.on('devtools-opened', () => { + if (!isDev) mainWindow.webContents.closeDevTools() + }) } // This method will be called when Electron has finished diff --git a/electron/preload.ts b/electron/preload.ts index 4e332b8..0d6f251 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -5,22 +5,21 @@ import { contextBridge, ipcRenderer } from 'electron' contextBridge.exposeInMainWorld('walletApi', { createKeystore: (seed: string, password: string) => ipcRenderer.invoke('wallet:createKeystore', seed, password), - saveKeystoreAs: (seed: string, password: string) => - ipcRenderer.invoke('wallet:saveKeystoreAs', seed, password), - decryptKeystore: (filePath: string, password: string) => - ipcRenderer.invoke('wallet:decryptKeystore', filePath, password), + saveKeystoreAs: (fileName: string) => ipcRenderer.invoke('wallet:saveKeystoreAs', fileName), + decryptKeystore: (fileName: string, password: string) => + ipcRenderer.invoke('wallet:decryptKeystore', fileName, password), checkKeystore: () => ipcRenderer.invoke('wallet:checkKeystore'), generateKeysFromSeed: (seedPhrase: string[]) => ipcRenderer.invoke('wallet:generateKeysFromSeed', seedPhrase), buildTransaction: (args: any) => ipcRenderer.invoke('wallet:buildTransaction', args), - updateMinBlockHeight: (filePath: string | null, minBlockHeight: number | null) => - ipcRenderer.invoke('wallet:updateMinBlockHeight', filePath, minBlockHeight), - getMinBlockHeight: (filePath: string | null) => - ipcRenderer.invoke('wallet:getMinBlockHeight', filePath), + updateMinBlockHeight: (fileName: string | null, minBlockHeight: number | null) => + ipcRenderer.invoke('wallet:updateMinBlockHeight', fileName, minBlockHeight), + getMinBlockHeight: (fileName: string | null) => + ipcRenderer.invoke('wallet:getMinBlockHeight', fileName), }) contextBridge.exposeInMainWorld('logger', { - info: (msg: string) => ipcRenderer.send('log:info', msg), - warn: (msg: string) => ipcRenderer.send('log:warn', msg), - error: (msg: string) => ipcRenderer.send('log:error', msg), + info: (...msg: any[]) => ipcRenderer.send('log:info', ...msg), + warn: (...msg: any[]) => ipcRenderer.send('log:warn', ...msg), + error: (...msg: any[]) => ipcRenderer.send('log:error', ...msg), }) diff --git a/index.html b/index.html index 0933a1a..b26720e 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,15 @@ + + Neptune Web Wallet diff --git a/package-lock.json b/package-lock.json index 70e1f78..1ce09b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,11 +15,10 @@ "@neptune/wasm": "file:./packages/neptune-wasm", "ant-design-vue": "^4.2.6", "axios": "^1.6.8", - "crypto": "^1.0.1", "electron-log": "^5.4.3", "electron-squirrel-startup": "^1.0.1", "pinia": "^2.1.7", - "vue": "^3.4.21", + "vue": "^3.5.24", "vue-router": "^4.3.0", "vue3-i18n": "^1.1.5" }, @@ -33,12 +32,12 @@ "@electron-forge/plugin-fuses": "^7.10.2", "@electron-forge/plugin-vite": "^7.10.2", "@electron/fuses": "^1.8.0", - "@rushstack/eslint-patch": "^1.8.0", + "@rushstack/eslint-patch": "^1.15.0", "@tsconfig/node20": "^20.1.4", "@types/electron-squirrel-startup": "^1.0.2", - "@types/node": "^20.12.5", + "@types/node": "^20.19.25", "@vitejs/plugin-vue": "^5.0.4", - "@vitejs/plugin-vue-jsx": "^3.1.0", + "@vitejs/plugin-vue-jsx": "^5.1.1", "@vue/eslint-config-prettier": "^9.0.0", "@vue/eslint-config-typescript": "^13.0.0", "@vue/tsconfig": "^0.5.1", @@ -47,11 +46,11 @@ "eslint-plugin-vue": "^9.23.0", "npm-run-all2": "^6.1.2", "prettier": "^3.2.5", - "sass": "^1.75.0", + "sass": "^1.94.0", "typescript": "~5.4.0", "unplugin-auto-import": "^20.2.0", "unplugin-vue-components": "^30.0.0", - "vite": "^5.2.8", + "vite": "^5.4.21", "vite-plugin-vue-devtools": "^7.0.25", "vue-tsc": "^2.0.11" } @@ -1977,9 +1976,9 @@ } }, "node_modules/@inquirer/core/node_modules/@types/node": { - "version": "22.19.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.0.tgz", - "integrity": "sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA==", + "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2017,9 +2016,9 @@ } }, "node_modules/@inquirer/figures": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.14.tgz", - "integrity": "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ==", + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", "dev": true, "license": "MIT", "engines": { @@ -2700,6 +2699,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.50", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.50.tgz", + "integrity": "sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/pluginutils": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", @@ -2737,9 +2743,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", - "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz", + "integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==", "cpu": [ "arm" ], @@ -2751,9 +2757,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz", - "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz", + "integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==", "cpu": [ "arm64" ], @@ -2765,9 +2771,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz", - "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz", + "integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==", "cpu": [ "arm64" ], @@ -2779,9 +2785,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz", - "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz", + "integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==", "cpu": [ "x64" ], @@ -2793,9 +2799,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz", - "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz", + "integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==", "cpu": [ "arm64" ], @@ -2807,9 +2813,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz", - "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz", + "integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==", "cpu": [ "x64" ], @@ -2821,9 +2827,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz", - "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz", + "integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==", "cpu": [ "arm" ], @@ -2835,9 +2841,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz", - "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz", + "integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==", "cpu": [ "arm" ], @@ -2849,9 +2855,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz", - "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz", + "integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==", "cpu": [ "arm64" ], @@ -2863,9 +2869,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz", - "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz", + "integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==", "cpu": [ "arm64" ], @@ -2877,9 +2883,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz", - "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz", + "integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==", "cpu": [ "loong64" ], @@ -2891,9 +2897,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz", - "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz", + "integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==", "cpu": [ "ppc64" ], @@ -2905,9 +2911,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz", - "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz", + "integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==", "cpu": [ "riscv64" ], @@ -2919,9 +2925,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz", - "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz", + "integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==", "cpu": [ "riscv64" ], @@ -2933,9 +2939,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz", - "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz", + "integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==", "cpu": [ "s390x" ], @@ -2947,9 +2953,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz", - "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz", + "integrity": "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==", "cpu": [ "x64" ], @@ -2961,9 +2967,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", - "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz", + "integrity": "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==", "cpu": [ "x64" ], @@ -2975,9 +2981,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz", - "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz", + "integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==", "cpu": [ "arm64" ], @@ -2989,9 +2995,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz", - "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz", + "integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==", "cpu": [ "arm64" ], @@ -3003,9 +3009,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz", - "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz", + "integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==", "cpu": [ "ia32" ], @@ -3017,9 +3023,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz", - "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz", + "integrity": "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==", "cpu": [ "x64" ], @@ -3031,9 +3037,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz", - "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz", + "integrity": "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==", "cpu": [ "x64" ], @@ -3045,9 +3051,9 @@ ] }, "node_modules/@rushstack/eslint-patch": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.14.1.tgz", - "integrity": "sha512-jGTk8UD/RdjsNZW8qq10r0RBvxL8OWtoT+kImlzPDFilmozzM+9QmIJsmze9UiSBrFU45ZxhTYBypn9q9z/VfQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.15.0.tgz", + "integrity": "sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw==", "dev": true, "license": "MIT" }, @@ -3118,9 +3124,9 @@ } }, "node_modules/@tsconfig/node20": { - "version": "20.1.6", - "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.6.tgz", - "integrity": "sha512-sz+Hqx9zwZDpZIV871WSbUzSqNIsXzghZydypnfgzPKLltVJfkINfUeTct31n/tTSa9ZE1ZOfKdRre1uHHquYQ==", + "version": "20.1.7", + "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.7.tgz", + "integrity": "sha512-Lt137u6AoCCJNNklpNtfGIYFEywma7Hd3B083jqMga37opSEVw7beWsD4OTXyzKS0I3G2kbQJVoZpOxy9GnxBQ==", "dev": true, "license": "MIT" }, @@ -3219,9 +3225,9 @@ } }, "node_modules/@types/node": { - "version": "20.19.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz", - "integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==", + "version": "20.19.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz", + "integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3497,21 +3503,23 @@ } }, "node_modules/@vitejs/plugin-vue-jsx": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-3.1.0.tgz", - "integrity": "sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-5.1.1.tgz", + "integrity": "sha512-uQkfxzlF8SGHJJVH966lFTdjM/lGcwJGzwAHpVqAPDD/QcsqoUGa+q31ox1BrUfi+FLP2ChVp7uLXE3DkHyDdQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.23.3", - "@babel/plugin-transform-typescript": "^7.23.3", - "@vue/babel-plugin-jsx": "^1.1.5" + "@babel/core": "^7.28.3", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.28.0", + "@rolldown/pluginutils": "^1.0.0-beta.34", + "@vue/babel-plugin-jsx": "^1.5.0" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^4.0.0 || ^5.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", "vue": "^3.0.0" } }, @@ -3605,39 +3613,39 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.23.tgz", - "integrity": "sha512-nW7THWj5HOp085ROk65LwaoxuzDsjIxr485F4iu63BoxsXoSqKqmsUUoP4A7Gl67DgIgi0zJ8JFgHfvny/74MA==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.24.tgz", + "integrity": "sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==", "license": "MIT", "dependencies": { "@babel/parser": "^7.28.5", - "@vue/shared": "3.5.23", + "@vue/shared": "3.5.24", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.23.tgz", - "integrity": "sha512-AT8RMw0vEzzzO0JU5gY0F6iCzaWUIh/aaRVordzMBKXRpoTllTT4kocHDssByPsvodNCfump/Lkdow2mT/O5KQ==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz", + "integrity": "sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.23", - "@vue/shared": "3.5.23" + "@vue/compiler-core": "3.5.24", + "@vue/shared": "3.5.24" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.23.tgz", - "integrity": "sha512-3QTEUo4qg7FtQwaDJa8ou1CUikx5WTtZlY61rRRDu3lK2ZKrGoAGG8mvDgOpDsQ4A1bez9s+WtBB6DS2KuFCPw==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.24.tgz", + "integrity": "sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==", "license": "MIT", "dependencies": { "@babel/parser": "^7.28.5", - "@vue/compiler-core": "3.5.23", - "@vue/compiler-dom": "3.5.23", - "@vue/compiler-ssr": "3.5.23", - "@vue/shared": "3.5.23", + "@vue/compiler-core": "3.5.24", + "@vue/compiler-dom": "3.5.24", + "@vue/compiler-ssr": "3.5.24", + "@vue/shared": "3.5.24", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", @@ -3645,13 +3653,13 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.23.tgz", - "integrity": "sha512-Hld2xphbMjXs9Q9WKxPf2EqmE+Rq/FEDnK/wUBtmYq74HCV4XDdSCheAaB823OQXIIFGq9ig/RbAZkF9s4U0Ow==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz", + "integrity": "sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.23", - "@vue/shared": "3.5.23" + "@vue/compiler-dom": "3.5.24", + "@vue/shared": "3.5.24" } }, "node_modules/@vue/compiler-vue2": { @@ -3672,14 +3680,14 @@ "license": "MIT" }, "node_modules/@vue/devtools-core": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.7.7.tgz", - "integrity": "sha512-9z9TLbfC+AjAi1PQyWX+OErjIaJmdFlbDHcD+cAMYKY6Bh5VlsAtCeGyRMrXwIlMEQPukvnWt3gZBLwTAIMKzQ==", + "version": "7.7.8", + "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.7.8.tgz", + "integrity": "sha512-EVLQTYML/v77JFA3Q8zvVANCvEv1WtG0TMo+HQR5eZ7PpEzSmVbEcBp2C1/OXyn8EJO4mHEeParMLpp43prUOw==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-kit": "^7.7.7", - "@vue/devtools-shared": "^7.7.7", + "@vue/devtools-kit": "^7.7.8", + "@vue/devtools-shared": "^7.7.8", "mitt": "^3.0.1", "nanoid": "^5.1.0", "pathe": "^2.0.3", @@ -3709,13 +3717,13 @@ } }, "node_modules/@vue/devtools-kit": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz", - "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==", + "version": "7.7.8", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.8.tgz", + "integrity": "sha512-4Y8op+AoxOJhB9fpcEF6d5vcJXWKgHxC3B0ytUB8zz15KbP9g9WgVzral05xluxi2fOeAy6t140rdQ943GcLRQ==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-shared": "^7.7.7", + "@vue/devtools-shared": "^7.7.8", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", @@ -3725,9 +3733,9 @@ } }, "node_modules/@vue/devtools-shared": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz", - "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==", + "version": "7.7.8", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.8.tgz", + "integrity": "sha512-XHpO3jC5nOgYr40M9p8Z4mmKfTvUxKyRcUnpBAYg11pE78eaRFBKb0kG5yKLroMuJeeNH9LWmKp2zMU5LUc7CA==", "dev": true, "license": "MIT", "dependencies": { @@ -3826,53 +3834,53 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.5.23", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.23.tgz", - "integrity": "sha512-ji5w0qvrPyBmBx5Ldv4QGNsw0phgRreEvjt0iUf1lei2Sm8//9ZAi78uM2ZjsT5gk0YZilLuoRCIMvtuZlHMJw==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.24.tgz", + "integrity": "sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.23" + "@vue/shared": "3.5.24" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.23", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.23.tgz", - "integrity": "sha512-LMB0S6/G7mFJcpQeQaZrbsthFbWrIX8FVTzu5x9U3Ec8YW5MY1CGAnBBHNj+TPOBu3pIbtPpjrXtcaN04X+aBw==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.24.tgz", + "integrity": "sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.23", - "@vue/shared": "3.5.23" + "@vue/reactivity": "3.5.24", + "@vue/shared": "3.5.24" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.23", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.23.tgz", - "integrity": "sha512-r/PYc8W9THzEL0UExpTkV+d31zO+Jid/RMZIDG6aS/NekOEUHuCJkJgftySWZw7JTJO/+q9Kxkg8p+i7Q7Q+ew==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.24.tgz", + "integrity": "sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.23", - "@vue/runtime-core": "3.5.23", - "@vue/shared": "3.5.23", + "@vue/reactivity": "3.5.24", + "@vue/runtime-core": "3.5.24", + "@vue/shared": "3.5.24", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.23", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.23.tgz", - "integrity": "sha512-NiWZsNCsXA20/VufcrW5u+Trt/PyFlpMmxaB2KERYM8eZgUoKUjXxJQb9ypq+LZ0Sp3XHJGNBR8DkhRnkKAMUw==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.24.tgz", + "integrity": "sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.23", - "@vue/shared": "3.5.23" + "@vue/compiler-ssr": "3.5.24", + "@vue/shared": "3.5.24" }, "peerDependencies": { - "vue": "3.5.23" + "vue": "3.5.24" } }, "node_modules/@vue/shared": { - "version": "3.5.23", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.23.tgz", - "integrity": "sha512-0YZ1DYuC5o/YJPf6pFdt2KYxVGDxkDbH/1NYJnVJWUkzr8ituBEmFVQRNX2gCaAsFEjEDnLkWpgqlZA7htgS/g==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz", + "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==", "license": "MIT" }, "node_modules/@vue/tsconfig": { @@ -4393,9 +4401,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.25", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz", - "integrity": "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==", + "version": "2.8.27", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.27.tgz", + "integrity": "sha512-2CXFpkjVnY2FT+B6GrSYxzYf65BJWEqz5tIRHCvNsZZ2F3CmsCB37h8SpYgKG7y9C4YAeTipIPWG7EmFmhAeXA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4403,9 +4411,9 @@ } }, "node_modules/birpc": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.7.0.tgz", - "integrity": "sha512-tub/wFGH49vNCm0xraykcY3TcRgX/3JsALYq/Lwrtti+bTyFHkCUAWF5wgYoie8P41wYwig2mIKiqoocr1EkEQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.8.0.tgz", + "integrity": "sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==", "dev": true, "license": "MIT", "funding": { @@ -4472,9 +4480,9 @@ } }, "node_modules/browserslist": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", - "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", + "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", "dev": true, "funding": [ { @@ -4492,10 +4500,10 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.8.19", - "caniuse-lite": "^1.0.30001751", - "electron-to-chromium": "^1.5.238", - "node-releases": "^2.0.26", + "baseline-browser-mapping": "^2.8.25", + "caniuse-lite": "^1.0.30001754", + "electron-to-chromium": "^1.5.249", + "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": { @@ -5093,13 +5101,6 @@ "node": ">=12.10" } }, - "node_modules/crypto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", - "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", - "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.", - "license": "ISC" - }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -5187,9 +5188,9 @@ "license": "MIT" }, "node_modules/default-browser": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", - "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.3.0.tgz", + "integrity": "sha512-Qq68+VkJlc8tjnPV1i7HtbIn7ohmjZa88qUvHMIK0ZKUXMCuV45cT7cEXALPUmeXCe0q1DWQkQTemHVaLIFSrg==", "dev": true, "license": "MIT", "dependencies": { @@ -5388,9 +5389,9 @@ "license": "MIT" }, "node_modules/electron": { - "version": "39.1.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-39.1.0.tgz", - "integrity": "sha512-vPRbKKQUzKWZZX68fuYdz4iS/eavGcQkHOGK4ylv0YJLbBRxxUlflPRdqRGflFjwid+sja7gbNul2lArevYwrw==", + "version": "39.1.2", + "resolved": "https://registry.npmjs.org/electron/-/electron-39.1.2.tgz", + "integrity": "sha512-+/TwT9NWxyQGTm5WemJEJy+bWCpnKJ4PLPswI1yn1P63bzM0/8yAeG05yS+NfFaWH4yNQtGXZmAv87Bxa5RlLg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5842,9 +5843,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.245", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.245.tgz", - "integrity": "sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ==", + "version": "1.5.250", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.250.tgz", + "integrity": "sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw==", "dev": true, "license": "ISC" }, @@ -5931,9 +5932,9 @@ } }, "node_modules/electron/node_modules/@types/node": { - "version": "22.19.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.0.tgz", - "integrity": "sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA==", + "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6573,9 +6574,9 @@ "license": "Apache-2.0" }, "node_modules/exsolve": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", - "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", "dev": true, "license": "MIT" }, @@ -7575,9 +7576,9 @@ } }, "node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "dev": true, "license": "MIT", "engines": { @@ -7857,9 +7858,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "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": { @@ -10095,9 +10096,9 @@ } }, "node_modules/rollup": { - "version": "4.52.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", - "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz", + "integrity": "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==", "dev": true, "license": "MIT", "dependencies": { @@ -10111,28 +10112,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.52.5", - "@rollup/rollup-android-arm64": "4.52.5", - "@rollup/rollup-darwin-arm64": "4.52.5", - "@rollup/rollup-darwin-x64": "4.52.5", - "@rollup/rollup-freebsd-arm64": "4.52.5", - "@rollup/rollup-freebsd-x64": "4.52.5", - "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", - "@rollup/rollup-linux-arm-musleabihf": "4.52.5", - "@rollup/rollup-linux-arm64-gnu": "4.52.5", - "@rollup/rollup-linux-arm64-musl": "4.52.5", - "@rollup/rollup-linux-loong64-gnu": "4.52.5", - "@rollup/rollup-linux-ppc64-gnu": "4.52.5", - "@rollup/rollup-linux-riscv64-gnu": "4.52.5", - "@rollup/rollup-linux-riscv64-musl": "4.52.5", - "@rollup/rollup-linux-s390x-gnu": "4.52.5", - "@rollup/rollup-linux-x64-gnu": "4.52.5", - "@rollup/rollup-linux-x64-musl": "4.52.5", - "@rollup/rollup-openharmony-arm64": "4.52.5", - "@rollup/rollup-win32-arm64-msvc": "4.52.5", - "@rollup/rollup-win32-ia32-msvc": "4.52.5", - "@rollup/rollup-win32-x64-gnu": "4.52.5", - "@rollup/rollup-win32-x64-msvc": "4.52.5", + "@rollup/rollup-android-arm-eabi": "4.53.2", + "@rollup/rollup-android-arm64": "4.53.2", + "@rollup/rollup-darwin-arm64": "4.53.2", + "@rollup/rollup-darwin-x64": "4.53.2", + "@rollup/rollup-freebsd-arm64": "4.53.2", + "@rollup/rollup-freebsd-x64": "4.53.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.2", + "@rollup/rollup-linux-arm-musleabihf": "4.53.2", + "@rollup/rollup-linux-arm64-gnu": "4.53.2", + "@rollup/rollup-linux-arm64-musl": "4.53.2", + "@rollup/rollup-linux-loong64-gnu": "4.53.2", + "@rollup/rollup-linux-ppc64-gnu": "4.53.2", + "@rollup/rollup-linux-riscv64-gnu": "4.53.2", + "@rollup/rollup-linux-riscv64-musl": "4.53.2", + "@rollup/rollup-linux-s390x-gnu": "4.53.2", + "@rollup/rollup-linux-x64-gnu": "4.53.2", + "@rollup/rollup-linux-x64-musl": "4.53.2", + "@rollup/rollup-openharmony-arm64": "4.53.2", + "@rollup/rollup-win32-arm64-msvc": "4.53.2", + "@rollup/rollup-win32-ia32-msvc": "4.53.2", + "@rollup/rollup-win32-x64-gnu": "4.53.2", + "@rollup/rollup-win32-x64-msvc": "4.53.2", "fsevents": "~2.3.2" } }, @@ -10202,9 +10203,9 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.93.3", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.3.tgz", - "integrity": "sha512-elOcIZRTM76dvxNAjqYrucTSI0teAF/L2Lv0s6f6b7FOwcwIuA357bIE871580AjHJuSvLIRUosgV+lIWx6Rgg==", + "version": "1.94.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.0.tgz", + "integrity": "sha512-Dqh7SiYcaFtdv5Wvku6QgS5IGPm281L+ZtVD1U2FJa7Q0EFRlq8Z3sjYtz6gYObsYThUOz9ArwFqPZx+1azILQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11665,15 +11666,15 @@ } }, "node_modules/vite-plugin-vue-devtools": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.7.7.tgz", - "integrity": "sha512-d0fIh3wRcgSlr4Vz7bAk4va1MkdqhQgj9ANE/rBhsAjOnRfTLs2ocjFMvSUOsv6SRRXU9G+VM7yMgqDb6yI4iQ==", + "version": "7.7.8", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.7.8.tgz", + "integrity": "sha512-04jowFsal5f9Gbso0X5Ff/mtvik7VP/PBYcKDCQHnTLH0x+juWSj7v1QJfDtXnWrrxU7/yrljEP8KZTm4skvkg==", "dev": true, "license": "MIT", "dependencies": { - "@vue/devtools-core": "^7.7.7", - "@vue/devtools-kit": "^7.7.7", - "@vue/devtools-shared": "^7.7.7", + "@vue/devtools-core": "^7.7.8", + "@vue/devtools-kit": "^7.7.8", + "@vue/devtools-shared": "^7.7.8", "execa": "^9.5.2", "sirv": "^3.0.1", "vite-plugin-inspect": "0.8.9", @@ -11802,16 +11803,16 @@ "license": "MIT" }, "node_modules/vue": { - "version": "3.5.23", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.23.tgz", - "integrity": "sha512-CfvZv/vI52xUhumUvHtD6iFIS78nGWfX4IJnHfBGhpqMI0CwDq2YEngXOeaBFMRmiArcqczuVrLxurvesTYT9w==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.24.tgz", + "integrity": "sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.23", - "@vue/compiler-sfc": "3.5.23", - "@vue/runtime-dom": "3.5.23", - "@vue/server-renderer": "3.5.23", - "@vue/shared": "3.5.23" + "@vue/compiler-dom": "3.5.24", + "@vue/compiler-sfc": "3.5.24", + "@vue/runtime-dom": "3.5.24", + "@vue/server-renderer": "3.5.24", + "@vue/shared": "3.5.24" }, "peerDependencies": { "typescript": "*" diff --git a/package.json b/package.json index f364fef..f13da4f 100644 --- a/package.json +++ b/package.json @@ -25,11 +25,10 @@ "@neptune/wasm": "file:./packages/neptune-wasm", "ant-design-vue": "^4.2.6", "axios": "^1.6.8", - "crypto": "^1.0.1", "electron-log": "^5.4.3", "electron-squirrel-startup": "^1.0.1", "pinia": "^2.1.7", - "vue": "^3.4.21", + "vue": "^3.5.24", "vue-router": "^4.3.0", "vue3-i18n": "^1.1.5" }, @@ -43,12 +42,12 @@ "@electron-forge/plugin-fuses": "^7.10.2", "@electron-forge/plugin-vite": "^7.10.2", "@electron/fuses": "^1.8.0", - "@rushstack/eslint-patch": "^1.8.0", + "@rushstack/eslint-patch": "^1.15.0", "@tsconfig/node20": "^20.1.4", "@types/electron-squirrel-startup": "^1.0.2", - "@types/node": "^20.12.5", + "@types/node": "^20.19.25", "@vitejs/plugin-vue": "^5.0.4", - "@vitejs/plugin-vue-jsx": "^3.1.0", + "@vitejs/plugin-vue-jsx": "^5.1.1", "@vue/eslint-config-prettier": "^9.0.0", "@vue/eslint-config-typescript": "^13.0.0", "@vue/tsconfig": "^0.5.1", @@ -57,11 +56,11 @@ "eslint-plugin-vue": "^9.23.0", "npm-run-all2": "^6.1.2", "prettier": "^3.2.5", - "sass": "^1.75.0", + "sass": "^1.94.0", "typescript": "~5.4.0", "unplugin-auto-import": "^20.2.0", "unplugin-vue-components": "^30.0.0", - "vite": "^5.2.8", + "vite": "^5.4.21", "vite-plugin-vue-devtools": "^7.0.25", "vue-tsc": "^2.0.11" } diff --git a/public/favicon.png b/public/favicon.png index c049b11..679e06b 100644 Binary files a/public/favicon.png and b/public/favicon.png differ diff --git a/src/api/config/index.ts b/src/api/config/index.ts index 4a93c26..f3aa57c 100644 --- a/src/api/config/index.ts +++ b/src/api/config/index.ts @@ -11,15 +11,6 @@ const instance = axios.create({ }, }) -instance.interceptors.response.use( - (response) => { - return response - }, - (error) => { - return Promise.reject(error) - } -) - instance.interceptors.response.use( function (response) { if (response?.status !== STATUS_CODE_SUCCESS) return Promise.reject(response?.data) diff --git a/src/api/neptuneApi.ts b/src/api/neptuneApi.ts index 883a9c9..9dbde44 100644 --- a/src/api/neptuneApi.ts +++ b/src/api/neptuneApi.ts @@ -1,8 +1,9 @@ import { callJsonRpc } from '@/api/request' +import { DEFAULT_MIN_BLOCK_HEIGHT } from '@/utils/constants/constants' export const getUtxosFromViewKey = async ( viewKey: string, - startBlock: number | null = 0, + startBlock: number = DEFAULT_MIN_BLOCK_HEIGHT, endBlock: number | null = null, maxSearchDepth: number = 1000 ): Promise => { @@ -17,7 +18,7 @@ export const getUtxosFromViewKey = async ( export const getBalance = async ( viewKey: string, - startBlock: number | null = 0, + startBlock: number = DEFAULT_MIN_BLOCK_HEIGHT, endBlock: number | null = null, maxSearchDepth: number = 1000 ): Promise => { diff --git a/src/assets/imgs/logo.png b/src/assets/imgs/logo.png index c049b11..679e06b 100644 Binary files a/src/assets/imgs/logo.png and b/src/assets/imgs/logo.png differ diff --git a/src/composables/useNeptuneWallet.ts b/src/composables/useNeptuneWallet.ts index 94eec43..3832424 100644 --- a/src/composables/useNeptuneWallet.ts +++ b/src/composables/useNeptuneWallet.ts @@ -1,13 +1,14 @@ import { useNeptuneStore } from '@/stores/neptuneStore' import * as API from '@/api/neptuneApi' import type { + BalanceResult, GenerateSeedResult, PayloadBuildTransaction, ViewKeyResult, WalletState, } from '@/interface' import initWasm, { generate_seed, address_from_seed, validate_seed_phrase } from '@neptune/wasm' -import { toFiniteNumber } from '@/utils' +import { DEFAULT_MIN_BLOCK_HEIGHT, toFiniteNumber } from '@/utils' let wasmInitialized = false let initPromise: Promise | null = null @@ -32,7 +33,7 @@ export function useNeptuneWallet() { wasmInitialized = true } catch (err: any) { wasmInitialized = false - await (window as any).logger.error('WASM init error:', err.message) + await (window as any).logger.error('WASM init error:') throw new Error('Failed to initialize Neptune WASM') } })() @@ -47,7 +48,6 @@ export function useNeptuneWallet() { const resultJson = generate_seed() const result: GenerateSeedResult = JSON.parse(resultJson) - store.setSeedPhrase(result.seed_phrase) store.setReceiverId(result.receiver_identifier) const viewKeyResult = await getViewKeyFromSeed(result.seed_phrase) @@ -59,24 +59,22 @@ export function useNeptuneWallet() { return result } catch (err: any) { - await (window as any).logger.error('Error generating wallet:', err.message) + await (window as any).logger.error('Error generating wallet: ', err.message) throw err } } const getViewKeyFromSeed = async (seedPhrase: string[]): Promise => { - const result = await (window as any).walletApi.generateKeysFromSeed([...seedPhrase]) - return JSON.parse(result) + return await (window as any).walletApi.generateKeysFromSeed([...seedPhrase]) } const recoverWalletFromSeed = async (seedPhrase: string[]): Promise => { try { + await ensureWasmInitialized() const isValid = validate_seed_phrase(JSON.stringify(seedPhrase)) if (!isValid) throw new Error('Invalid seed phrase') const result = await getViewKeyFromSeed(seedPhrase) - - store.setSeedPhrase(seedPhrase) store.setReceiverId(result.receiver_identifier) store.setViewKey(result.view_key_hex) store.setSpendingKey(result.spending_key_hex) @@ -85,7 +83,6 @@ export function useNeptuneWallet() { store.setAddress(addressResult) return { - seedPhrase: seedPhrase, network: store.getNetwork, receiverId: result.receiver_identifier, viewKey: result.view_key_hex, @@ -93,7 +90,7 @@ export function useNeptuneWallet() { address: addressResult, } } catch (err: any) { - await (window as any).logger.error('Error recovering wallet from seed:', err.message) + await (window as any).logger.error('Error recovering wallet from seed: ', err.message) throw err } } @@ -104,21 +101,19 @@ export function useNeptuneWallet() { return address_from_seed(seedPhraseJson, store.getNetwork) } - const decryptKeystore = async (password: string): Promise => { + const decryptKeystore = async (password: string): Promise<{ seedPhrase: string[] }> => { try { - const keystorePath = store.getKeystorePath - if (!keystorePath) await checkKeystore() + const keystoreFileName = store.getKeystoreFileName + if (!keystoreFileName) await checkKeystore() const result = await (window as any).walletApi.decryptKeystore( - store.getKeystorePath, + keystoreFileName, password ) const seedPhrase = result.phrase.trim().split(/\s+/) const viewKeyResult = await getViewKeyFromSeed(seedPhrase) - store.setPassword(password) - store.setSeedPhrase(seedPhrase) store.setViewKey(viewKeyResult.view_key_hex) store.setReceiverId(viewKeyResult.receiver_identifier) store.setSpendingKey(viewKeyResult.spending_key_hex) @@ -127,6 +122,8 @@ export function useNeptuneWallet() { store.setAddress(addressResult) await loadMinBlockHeightFromKeystore() + + return { seedPhrase } } catch (err: any) { if ( err instanceof Error && @@ -134,55 +131,58 @@ export function useNeptuneWallet() { err.message.includes('unable to authenticate')) ) { await (window as any).logger.error('Invalid password') - } else await (window as any).logger.error('Error decrypting keystore:', err.message) + } else await (window as any).logger.error('Error decrypting keystore: ', err.message) throw err } } - const createKeystore = async (seed: string, password: string): Promise => { + const createKeystore = async (seed: string, password: string): Promise => { try { const result = await (window as any).walletApi.createKeystore(seed, password) - store.setKeystorePath(result.filePath) - store.setMinBlockHeight(null) - return result.filePath + if (!result.success) return null + store.setKeystoreFileName(result.fileName) + store.setMinBlockHeight(DEFAULT_MIN_BLOCK_HEIGHT) + return result.fileName } catch (err: any) { - await (window as any).logger.error('Error creating keystore:', err.message) + await (window as any).logger.error('Error creating keystore: ', err.message) throw err } } - const saveKeystoreAs = async (seed: string, password: string): Promise => { + const saveKeystoreAs = async (): Promise => { try { - const result = await (window as any).walletApi.saveKeystoreAs(seed, password) + const keystoreFileName = store.getKeystoreFileName + if (!keystoreFileName) throw new Error('No file to save') + + const result = await (window as any).walletApi.saveKeystoreAs(keystoreFileName) if (!result.filePath) throw new Error('User canceled') - return result.filePath } catch (err: any) { - await (window as any).logger.error('Error saving keystore:', err.message) + await (window as any).logger.error('Error saving keystore: ', err.message) throw err } } const checkKeystore = async (): Promise => { try { - const keystoreFile = await (window as any).walletApi.checkKeystore() + const keystoreFile = await (window as any).walletApi.checkKeystore(store.getKeystoreFileName) if (!keystoreFile.exists) return false - store.setKeystorePath(keystoreFile.filePath) + store.setKeystoreFileName(keystoreFile.fileName) if ('minBlockHeight' in keystoreFile) { const height = keystoreFile.minBlockHeight store.setMinBlockHeight(toFiniteNumber(height)) } return true } catch (err: any) { - await (window as any).logger.error('Error checking keystore:', err.message) + await (window as any).logger.error('Error checking keystore: ', err.message) throw err } } const persistMinBlockHeight = async (utxos: any[]) => { - const keystorePath = store.getKeystorePath - if (!keystorePath) return + const keystoreFileName = store.getKeystoreFileName + if (!keystoreFileName) return try { const minBlockHeight = utxos.reduce((min, utxo) => { @@ -196,31 +196,31 @@ export function useNeptuneWallet() { }, null) const response = await (window as any).walletApi.updateMinBlockHeight( - keystorePath, + keystoreFileName, minBlockHeight ) if (!response.success) throw new Error('Failed to update min block height') store.setMinBlockHeight(minBlockHeight) } catch (err: any) { - await (window as any).logger.error('Error saving min block height:', err.message) + await (window as any).logger.error('Error saving min block height: ', err.message) throw err } } - const loadMinBlockHeightFromKeystore = async (): Promise => { - const keystorePath = store.getKeystorePath - if (!keystorePath) return null + const loadMinBlockHeightFromKeystore = async (): Promise => { + const keystoreFileName = store.getKeystoreFileName + if (!keystoreFileName) return DEFAULT_MIN_BLOCK_HEIGHT try { - const response = await (window as any).walletApi.getMinBlockHeight(keystorePath) - if (!response?.success) return null + const response = await (window as any).walletApi.getMinBlockHeight(keystoreFileName) + if (!response?.success) throw new Error(String(response.error)) const minBlockHeight = toFiniteNumber(response.minBlockHeight) store.setMinBlockHeight(minBlockHeight) return minBlockHeight } catch (err: any) { - await (window as any).logger.error('Error loading min block height:', err.message) + await (window as any).logger.error('Error loading min block height: ', err.message) throw err } } @@ -237,7 +237,7 @@ export function useNeptuneWallet() { const response = await API.getUtxosFromViewKey( store.getViewKey || '', - toFiniteNumber(startBlock, 0) + toFiniteNumber(startBlock) ) const result = response?.result || response @@ -249,22 +249,22 @@ export function useNeptuneWallet() { await persistMinBlockHeight(utxoList) return result } catch (err: any) { - await (window as any).logger.error('Error getting UTXOs:', err.message) + await (window as any).logger.error('Error getting UTXOs: ', err.message) throw err } } - const getBalance = async (): Promise => { + const getBalance = async (): Promise => { try { let startBlock: number | null | undefined = store.getMinBlockHeight - if (startBlock === null || startBlock === undefined) { + if (startBlock == null) { startBlock = await loadMinBlockHeightFromKeystore() } const response = await API.getBalance( store.getViewKey || '', - toFiniteNumber(startBlock, 0) + toFiniteNumber(startBlock) ) const result = response?.result || response @@ -275,7 +275,7 @@ export function useNeptuneWallet() { pendingBalance: result?.pendingBalance || result, } } catch (err: any) { - await (window as any).logger.error('Error getting balance:', err.message) + await (window as any).logger.error('Error getting balance: ', err.message) throw err } } @@ -286,7 +286,7 @@ export function useNeptuneWallet() { const result = response?.result || response return result?.height || result } catch (err: any) { - await (window as any).logger.error('Error getting block height:', err.message) + await (window as any).logger.error('Error getting block height: ', err.message) throw err } } @@ -299,7 +299,7 @@ export function useNeptuneWallet() { return result } catch (err: any) { - await (window as any).logger.error('Error getting network info:', err.message) + await (window as any).logger.error('Error getting network info: ', err.message) throw err } } @@ -313,7 +313,7 @@ export function useNeptuneWallet() { const payload = { spendingKeyHex: store.getSpendingKey, inputAdditionRecords: args.inputAdditionRecords, - minBlockHeight: toFiniteNumber(minBlockHeight, 0), + minBlockHeight: toFiniteNumber(minBlockHeight), outputAddresses: args.outputAddresses, outputAmounts: args.outputAmounts, fee: args.fee, @@ -327,25 +327,7 @@ export function useNeptuneWallet() { const result = response?.result || response return result } catch (err: any) { - await (window as any).logger.error('Error sending transaction:', err.message) - throw err - } - } - - const setNetwork = async (network: 'mainnet' | 'testnet') => { - try { - store.setNetwork(network) - - if (store.getSeedPhrase) { - const viewKeyResult = await getViewKeyFromSeed(store.getSeedPhrase) - store.setViewKey(viewKeyResult.view_key_hex) - store.setSpendingKey(viewKeyResult.spending_key_hex) - - const addressResult = await getAddressFromSeed(store.getSeedPhrase) - store.setAddress(addressResult) - } - } catch (err: any) { - await (window as any).logger.error('Error setting network:', err.message) + await (window as any).logger.error('Error sending transaction: ', err.message) throw err } } @@ -374,6 +356,5 @@ export function useNeptuneWallet() { checkKeystore, clearWallet, - setNetwork, } } diff --git a/src/interface/neptune.ts b/src/interface/neptune.ts index e8066ce..a017608 100644 --- a/src/interface/neptune.ts +++ b/src/interface/neptune.ts @@ -1,6 +1,4 @@ export interface WalletState { - seedPhrase: string[] | null - password?: string | null receiverId: string | null viewKey: string | null spendingKey?: string | null @@ -42,3 +40,8 @@ export interface Utxo { blockHeight: number utxoHash: string } + +export interface BalanceResult { + balance: string | null + pendingBalance: string | null +} diff --git a/src/stores/neptuneStore.ts b/src/stores/neptuneStore.ts index 6cb67ca..6d8dfb2 100644 --- a/src/stores/neptuneStore.ts +++ b/src/stores/neptuneStore.ts @@ -1,15 +1,13 @@ import { defineStore } from 'pinia' import { ref, computed } from 'vue' import type { WalletState } from '@/interface' -import { toFiniteNumber } from '@/utils' +import { DEFAULT_MIN_BLOCK_HEIGHT, toFiniteNumber } from '@/utils' export const useNeptuneStore = defineStore('neptune', () => { const defaultNetwork = (import.meta.env.VITE_NODE_NETWORK || 'mainnet') as 'mainnet' | 'testnet' // ===== STATE ===== const wallet = ref({ - seedPhrase: null, - password: null, receiverId: null, viewKey: null, spendingKey: null, @@ -21,18 +19,10 @@ export const useNeptuneStore = defineStore('neptune', () => { minBlockHeight: null, }) - const keystorePath = ref(null) + const keystoreFileName = ref(null) // ===== SETTERS ===== - const setSeedPhrase = (seedPhrase: string[] | null) => { - wallet.value.seedPhrase = seedPhrase - } - - const setPassword = (password: string | null) => { - wallet.value.password = password - } - const setReceiverId = (receiverId: string | null) => { wallet.value.receiverId = receiverId } @@ -65,7 +55,7 @@ export const useNeptuneStore = defineStore('neptune', () => { wallet.value.utxos = utxos } - const setMinBlockHeight = (minBlockHeight: number | null) => { + const setMinBlockHeight = (minBlockHeight: number = DEFAULT_MIN_BLOCK_HEIGHT) => { wallet.value.minBlockHeight = toFiniteNumber(minBlockHeight) } @@ -73,14 +63,12 @@ export const useNeptuneStore = defineStore('neptune', () => { wallet.value = { ...wallet.value, ...walletData } } - const setKeystorePath = (path: string | null) => { - keystorePath.value = path + const setKeystoreFileName = (fileName: string | null) => { + keystoreFileName.value = fileName } const clearWallet = () => { wallet.value = { - seedPhrase: null, - password: null, receiverId: null, viewKey: null, spendingKey: null, @@ -95,9 +83,6 @@ export const useNeptuneStore = defineStore('neptune', () => { // ===== GETTERS ===== const getWallet = computed(() => wallet.value) - const getSeedPhrase = computed(() => wallet.value.seedPhrase) - const getSeedPhraseString = computed(() => wallet.value.seedPhrase?.join(' ') || '') - const getPassword = computed(() => wallet.value.password) const getReceiverId = computed(() => wallet.value.receiverId) const getViewKey = computed(() => wallet.value.viewKey) const getSpendingKey = computed(() => wallet.value.spendingKey) @@ -108,12 +93,9 @@ export const useNeptuneStore = defineStore('neptune', () => { const getUtxos = computed(() => wallet.value.utxos) const getMinBlockHeight = computed(() => wallet.value.minBlockHeight ?? null) const hasWallet = computed(() => wallet.value.address !== null) - const getKeystorePath = computed(() => keystorePath.value) + const getKeystoreFileName = computed(() => keystoreFileName.value ?? null) return { getWallet, - getSeedPhrase, - getSeedPhraseString, - getPassword, getReceiverId, getViewKey, getSpendingKey, @@ -124,9 +106,7 @@ export const useNeptuneStore = defineStore('neptune', () => { getUtxos, getMinBlockHeight, hasWallet, - getKeystorePath, - setSeedPhrase, - setPassword, + getKeystoreFileName, setReceiverId, setViewKey, setSpendingKey, @@ -137,7 +117,7 @@ export const useNeptuneStore = defineStore('neptune', () => { setUtxos, setMinBlockHeight, setWallet, - setKeystorePath, + setKeystoreFileName, clearWallet, } }) diff --git a/src/utils/constants/constants.ts b/src/utils/constants/constants.ts index 725c7a3..52eac98 100644 --- a/src/utils/constants/constants.ts +++ b/src/utils/constants/constants.ts @@ -1,3 +1,4 @@ export const PAGE_FIRST = 1 export const PER_PAGE = 20 export const POLLING_INTERVAL = 1000 * 60 // 1 minute +export const DEFAULT_MIN_BLOCK_HEIGHT = 0 diff --git a/src/utils/helpers/helpers.ts b/src/utils/helpers/helpers.ts index a09ac63..1efc91a 100644 --- a/src/utils/helpers/helpers.ts +++ b/src/utils/helpers/helpers.ts @@ -1,3 +1,9 @@ -export function toFiniteNumber(value: number | null, defaultValue?: number): number | null { - return Number.isFinite(value) ? value : (defaultValue ?? null) +import { DEFAULT_MIN_BLOCK_HEIGHT } from '@/utils/constants/constants' + +export function toFiniteNumber( + value: number, + defaultValue: number = DEFAULT_MIN_BLOCK_HEIGHT +): number { + return Number.isFinite(value) ? value : defaultValue } + diff --git a/src/views/Auth/AuthView.vue b/src/views/Auth/AuthView.vue index 8789c08..59f5658 100644 --- a/src/views/Auth/AuthView.vue +++ b/src/views/Auth/AuthView.vue @@ -33,6 +33,7 @@ const handleGoToRecover = () => { diff --git a/src/views/Home/components/wallet-tab/SendTransactionComponent.vue b/src/views/Home/components/wallet-tab/SendTransactionComponent.vue index 32fbc88..6142043 100644 --- a/src/views/Home/components/wallet-tab/SendTransactionComponent.vue +++ b/src/views/Home/components/wallet-tab/SendTransactionComponent.vue @@ -1,9 +1,10 @@