diff --git a/.changeset/selfish-days-repeat.md b/.changeset/selfish-days-repeat.md new file mode 100644 index 0000000000..17f61e361b --- /dev/null +++ b/.changeset/selfish-days-repeat.md @@ -0,0 +1,5 @@ +--- +"fuels-wallet": patch +--- + +feat: send common errors automatically, instead of asking user to review diff --git a/examples/cra-dapp/package.json b/examples/cra-dapp/package.json index adfaecf44b..6550e16cd4 100644 --- a/examples/cra-dapp/package.json +++ b/examples/cra-dapp/package.json @@ -20,6 +20,6 @@ "@types/react-dom": "18.3.0", "@vitejs/plugin-react": "4.2.1", "typescript": "5.2.2", - "vite": "6.0.3" + "vite": "6.0.8" } } diff --git a/packages/app/playwright/e2e/ReportError.test.ts b/packages/app/playwright/e2e/ReportError.test.ts index 8fe2a04f9e..3463520f83 100644 --- a/packages/app/playwright/e2e/ReportError.test.ts +++ b/packages/app/playwright/e2e/ReportError.test.ts @@ -48,9 +48,11 @@ test.describe('ReportError', () => { await getByAriaLabel(page, 'Send error reports').click(); await expect(page.getByText(/Unexpected error/)).toHaveCount(0); - await page.waitForTimeout(2000); - const errorsAfterReporting = await getPageErrors(page); - expect(errorsAfterReporting.length).toBe(0); + await expect + .poll(async () => (await getPageErrors(page)).length, { + timeout: 10000, + }) + .toBe(0); }); test('should show Review Error in menu when there is a error in the database', async () => { @@ -59,7 +61,7 @@ test.describe('ReportError', () => { await window.fuelDB.errors.add({ id: '12345', error: { - name: 'React error', + name: 'React$ error', message: 'Test Error', stack: 'Line error 1', }, @@ -85,8 +87,8 @@ test.describe('ReportError', () => { await window.fuelDB.errors.add({ id: '12345', error: { - name: 'React error', - message: 'Test Error', + name: 'React$ error', + message: 'Test$ Error', stack: 'Line error 1', }, extra: { @@ -103,12 +105,16 @@ test.describe('ReportError', () => { page.locator(`[data-key="hasErrors"]`).click(); await hasText(page, /Unexpected error/i); + expect((await getPageErrors(page)).length).toBe(1); // report error await getByAriaLabel(page, 'Ignore error(s)').click(); await expect(page.getByText(/Unexpected error/i)).toHaveCount(0); - const errorsAfterReporting = await getPageErrors(page); - expect(errorsAfterReporting.length).toBe(1); + await expect + .poll(async () => (await getPageErrors(page)).length, { + timeout: 10000, + }) + .toBe(1); }); test('should be able to dismiss all errors', async () => { await visit(page, '/'); @@ -116,7 +122,7 @@ test.describe('ReportError', () => { await window.fuelDB.errors.add({ id: '12345', error: { - name: 'React error', + name: 'React$ error', message: 'Test Error', stack: 'Line error 1', }, @@ -141,8 +147,11 @@ test.describe('ReportError', () => { ).click(); await expect(page.getByText(/Unexpected error/i)).toHaveCount(0); - const errorsAfterReporting = await getPageErrors(page); - expect(errorsAfterReporting.length).toBe(0); + await expect + .poll(async () => (await getPageErrors(page)).length, { + timeout: 10000, + }) + .toBe(0); }); test('should hide when the single error is dismissed', async () => { await visit(page, '/'); @@ -150,7 +159,7 @@ test.describe('ReportError', () => { await window.fuelDB.errors.add({ id: '12345', error: { - name: 'React error', + name: 'React$ error', message: 'Test Error', stack: 'Line error 1', }, @@ -172,14 +181,17 @@ test.describe('ReportError', () => { await getByAriaLabel(page, 'Dismiss error').click(); await expect(page.getByText(/Unexpected error/i)).toHaveCount(0); - const errorsAfterReporting = await getPageErrors(page); - expect(errorsAfterReporting.length).toBe(0); + await expect + .poll(async () => (await getPageErrors(page)).length, { + timeout: 10000, + }) + .toBe(0); }); test('should detect and capture global errors', async () => { - await visit(page, '/'); await page.evaluate(async () => { - console.error(new Error('Test Error')); + console.error(new Error('New Error')); }); + await visit(page, '/'); await reload(page); await getByAriaLabel(page, 'Menu').click(); page.locator(`[data-key="hasErrors"]`).click(); @@ -195,7 +207,7 @@ test.describe('ReportError', () => { await window.fuelDB.errors.add({ id: '12345', error: { - name: 'React error', + name: 'React$ error', message: 'Test Error', stack: 'Line error 1', }, @@ -210,7 +222,7 @@ test.describe('ReportError', () => { await window.fuelDB.errors.add({ id: '123456', error: { - name: 'React error', + name: 'React$ error', message: 'Test Error', stack: 'Line error 1', }, @@ -231,4 +243,29 @@ test.describe('ReportError', () => { const errorsAfterReporting = await getPageErrors(page); expect(errorsAfterReporting.length).toBe(1); }); + test('should not show ignored errors', async () => { + await visit(page, '/'); + + await page.evaluate(async () => { + await window.fuelDB.errors.add({ + id: '12345', + error: { + name: 'React Error', + message: 'React Error', + stack: 'Line error 1', + }, + extra: { + timestamp: Date.now(), + location: 'http://localhost:3000', + pathname: '/', + hash: '#', + counts: 0, + }, + }); + }); + await getByAriaLabel(page, 'Menu').click(); + expect( + await page.locator(`[data-key="hasErrors"]`).isVisible() + ).toBeFalsy(); + }); }); diff --git a/packages/app/src/systems/Account/components/BalanceNFTs/constants.ts b/packages/app/src/systems/Account/components/BalanceNFTs/constants.ts index 335b16345e..b2c1aad024 100644 --- a/packages/app/src/systems/Account/components/BalanceNFTs/constants.ts +++ b/packages/app/src/systems/Account/components/BalanceNFTs/constants.ts @@ -1 +1 @@ -export const BALANCE_NFTS_TAB_HEIGHT = 244; \ No newline at end of file +export const BALANCE_NFTS_TAB_HEIGHT = 244; diff --git a/packages/app/src/systems/Account/components/QuickAccountConnect/QuickAccountConnect.tsx b/packages/app/src/systems/Account/components/QuickAccountConnect/QuickAccountConnect.tsx index 89747d9dca..468f151290 100644 --- a/packages/app/src/systems/Account/components/QuickAccountConnect/QuickAccountConnect.tsx +++ b/packages/app/src/systems/Account/components/QuickAccountConnect/QuickAccountConnect.tsx @@ -61,7 +61,9 @@ export const QuickAccountConnect = () => { const onDismiss = () => { if (!origin || !account) return; setDismissed(true); - localStorage.setItem(getDismissKey(account.address, origin.full), 'true'); + if (typeof localStorage !== 'undefined') { + localStorage.setItem(getDismissKey(account.address, origin.full), 'true'); + } }; useEffect(() => { diff --git a/packages/app/src/systems/Error/machines/reportErrorMachine.tsx b/packages/app/src/systems/Error/machines/reportErrorMachine.tsx index aa09754637..6dfc352820 100644 --- a/packages/app/src/systems/Error/machines/reportErrorMachine.tsx +++ b/packages/app/src/systems/Error/machines/reportErrorMachine.tsx @@ -4,6 +4,7 @@ import { assign, createMachine } from 'xstate'; import { db } from '~/systems/Core/utils/database'; import { ErrorProcessorService } from '~/systems/Error/services/ErrorProcessorService'; +import { getErrorIgnoreData } from '~/systems/Error/utils/getErrorIgnoreData'; import { ReportErrorService } from '../services'; export type ErrorMachineContext = { @@ -124,6 +125,9 @@ export const reportErrorMachine = createMachine( target: 'idle', }, ], + onError: { + target: 'idle', + }, }, }, reporting: { @@ -175,7 +179,13 @@ export const reportErrorMachine = createMachine( checkForErrors: async (context) => { await context.errorProcessorService.processErrors(); const hasErrors = await context.reportErrorService.checkForErrors(); - const errors = await context.reportErrorService.getErrors(); + const errors = (await context.reportErrorService.getErrors()).filter( + (e) => !getErrorIgnoreData(e?.error)?.action + ); + await context.reportErrorService.handleAndRemoveOldIgnoredErrors( + errors + ); + return { hasErrors, errors, diff --git a/packages/app/src/systems/Error/services/ReportErrorService.tsx b/packages/app/src/systems/Error/services/ReportErrorService.tsx index 4225ad8e43..530f725c70 100644 --- a/packages/app/src/systems/Error/services/ReportErrorService.tsx +++ b/packages/app/src/systems/Error/services/ReportErrorService.tsx @@ -1,6 +1,7 @@ import type { StoredFuelWalletError } from '@fuel-wallet/types'; -import * as Sentry from '@sentry/react'; import { db } from '~/systems/Core/utils/database'; +import { captureException } from '~/systems/Error/utils/captureException'; +import { getErrorIgnoreData } from '~/systems/Error/utils/getErrorIgnoreData'; import { parseFuelError } from '../utils'; export class ReportErrorService { @@ -12,29 +13,32 @@ export class ReportErrorService { if (typeof window !== 'undefined' && (window as any).playwright) { return; } - Sentry.captureException(e.error, { - extra: e.extra, - tags: { id: e.id, manual: true }, - }); + captureException(e.error, e.extra); } } static async saveError(error: Error) { - const parsedError = parseFuelError(error); - if (!parsedError) { - console.warn(`Can't save error without a message`); - return; - } - if (!('id' in parsedError)) { - console.warn(`Can't save error without an id`); - return; - } - if (!db.isOpen() || db.hasBeenClosed()) { - console.warn('Error saving error: db is closed'); - return; - } - try { + const parsedError = parseFuelError(error); + const ignoreData = getErrorIgnoreData(parsedError?.error); + if (!parsedError) { + console.warn(`Can't save error without a message`); + return; + } + if (!('id' in parsedError)) { + console.warn(`Can't save error without an id`); + return; + } + if (ignoreData?.action === 'ignore') return; + if (ignoreData?.action === 'hide') { + // Directly report to Sentry and exit + captureException(parsedError.error, parsedError.extra); + return; + } + if (!db.isOpen() || db.hasBeenClosed()) { + console.warn('Error saving error: db is closed'); + return; + } return await db.errors.add(parsedError); } catch (e) { console.warn('Failed to save error', e); @@ -43,17 +47,40 @@ export class ReportErrorService { async checkForErrors(): Promise { const errors = await this.getErrors(); - return errors.length > 0; + return ( + errors.filter((e) => !getErrorIgnoreData(e?.error)?.action).length > 0 + ); } async getErrors(): Promise { - return db.errors.toArray(); + return await db.errors.toArray(); } async clearErrors() { await db.errors.clear(); } + async handleAndRemoveOldIgnoredErrors(errors: StoredFuelWalletError[]) { + const errorsBeingRemoved: Array> = []; + // Convert to for of + for (const e of errors) { + const errorIgnoreData = getErrorIgnoreData(e?.error); + if (errorIgnoreData?.action) { + errorsBeingRemoved.push( + new Promise((resolve) => { + if (errorIgnoreData?.action === 'hide') { + captureException(e.error, e.extra); + } + return resolve( + this.dismissError(e.id).finally(() => resolve(true)) + ); + }) + ); + } + } + await Promise.all(errorsBeingRemoved); + } + async dismissError(key: string) { if (!key) return; db.errors.delete(key); diff --git a/packages/app/src/systems/Error/utils/captureException.ts b/packages/app/src/systems/Error/utils/captureException.ts new file mode 100644 index 0000000000..8dbb5b450a --- /dev/null +++ b/packages/app/src/systems/Error/utils/captureException.ts @@ -0,0 +1,9 @@ +import type { SentryExtraErrorData } from '@fuel-wallet/types'; +import * as Sentry from '@sentry/react'; + +export function captureException(error: Error, extra: SentryExtraErrorData) { + Sentry.captureException(error, { + extra, + tags: { manual: true }, + }); +} diff --git a/packages/app/src/systems/Error/utils/getErrorIgnoreData.ts b/packages/app/src/systems/Error/utils/getErrorIgnoreData.ts new file mode 100644 index 0000000000..bd7b499847 --- /dev/null +++ b/packages/app/src/systems/Error/utils/getErrorIgnoreData.ts @@ -0,0 +1,172 @@ +type IgnoredError = { + value: string; + field: 'message' | 'stack' | 'name'; + comparison: 'exact' | 'partial' | 'startsWith'; + /** + * @description Whether to ignore the error or hide it from Report Error screen. Avoid ignoring errors that might contain sensitive information. + */ + action: 'ignore' | 'hide'; +}; + +export function getErrorIgnoreData( + error: Error | undefined +): IgnoredError | undefined { + try { + return IGNORED_ERRORS.find((filter) => { + const errorValue = error?.[filter.field] as string | undefined; + + switch (filter.comparison) { + case 'exact': + return filter.value === errorValue; + case 'startsWith': + return errorValue?.startsWith(filter.value); + case 'partial': + return errorValue?.includes(filter.value); + } + }); + } catch (error) { + console.warn(error); + return undefined; + } +} + +const IGNORED_ERRORS: IgnoredError[] = [ + { + value: 'Out of sync', + field: 'message', + comparison: 'exact', + action: 'hide', + }, + { + value: 'Failed to fetch', + field: 'message', + comparison: 'exact', + action: 'hide', + }, + { + value: 'TypeError:', + field: 'stack', + comparison: 'startsWith', + action: 'hide', + }, + { + value: 'NotFoundError', + field: 'name', + comparison: 'exact', + action: 'hide', + }, + { + value: 'The browser is shutting down.', + field: 'message', + comparison: 'partial', + action: 'hide', + }, + { + value: 'Browser is shutting down.', + field: 'message', + comparison: 'partial', + action: 'hide', + }, + { + value: 'is not a function', + field: 'message', + comparison: 'partial', + action: 'hide', + }, + { + value: 'Asset ID already exists', + field: 'message', + comparison: 'partial', + action: 'ignore', + }, + { + value: 'Error fetching asset from db', + field: 'message', + comparison: 'partial', + action: 'hide', + }, + { + value: 'Cannot read properties of undefined', + field: 'message', + comparison: 'partial', + action: 'hide', + }, + { + value: 'Params are required', + field: 'message', + comparison: 'partial', + action: 'hide', + }, + { + value: 'Provider URL', + field: 'message', + comparison: 'partial', + action: 'hide', + }, + { + value: 'The above error occurred in the', + field: 'message', + comparison: 'partial', + action: 'hide', + }, + { + value: '[vite]', + field: 'message', + comparison: 'partial', + action: 'ignore', + }, + { + value: 'Function components cannot be given refs.', + field: 'message', + comparison: 'partial', + action: 'hide', + }, + { + value: 'Encountered two children with the same key', + field: 'message', + comparison: 'partial', + action: 'hide', + }, + { + value: 'Error fetching asset from db', + field: 'name', + comparison: 'partial', + action: 'hide', + }, + { + value: 'React Error', + field: 'name', + comparison: 'exact', + action: 'hide', + }, + { + value: 'React Error', + field: 'message', + comparison: 'partial', + action: 'hide', + }, + { + value: 'PopUp timed out waiting for event', + field: 'message', + comparison: 'partial', + action: 'hide', + }, + { + value: 'type is invalid', + field: 'message', + comparison: 'partial', + action: 'hide', + }, + { + value: 'Param name is required', + field: 'message', + comparison: 'partial', + action: 'hide', + }, + { + value: `Cannot access 'store' before initialization`, + field: 'message', + comparison: 'partial', + action: 'hide', + }, +]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a7014e0729..6be427e5e3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -161,13 +161,13 @@ importers: version: 18.3.0 '@vitejs/plugin-react': specifier: 4.2.1 - version: 4.2.1(vite@6.0.3(@types/node@22.10.1)(jiti@2.3.3)(terser@5.37.0)(yaml@2.6.1)) + version: 4.2.1(vite@6.0.8(@types/node@22.10.1)(jiti@2.3.3)(terser@5.37.0)(yaml@2.6.1)) typescript: specifier: 5.2.2 version: 5.2.2 vite: - specifier: 6.0.3 - version: 6.0.3(@types/node@22.10.1)(jiti@2.3.3)(terser@5.37.0)(yaml@2.6.1) + specifier: 6.0.8 + version: 6.0.8(@types/node@22.10.1)(jiti@2.3.3)(terser@5.37.0)(yaml@2.6.1) packages/app: dependencies: @@ -4699,191 +4699,96 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.28.1': - resolution: {integrity: sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==} - cpu: [arm] - os: [android] - '@rollup/rollup-android-arm-eabi@4.34.6': resolution: {integrity: sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.28.1': - resolution: {integrity: sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==} - cpu: [arm64] - os: [android] - '@rollup/rollup-android-arm64@4.34.6': resolution: {integrity: sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.28.1': - resolution: {integrity: sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==} - cpu: [arm64] - os: [darwin] - '@rollup/rollup-darwin-arm64@4.34.6': resolution: {integrity: sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.28.1': - resolution: {integrity: sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==} - cpu: [x64] - os: [darwin] - '@rollup/rollup-darwin-x64@4.34.6': resolution: {integrity: sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.28.1': - resolution: {integrity: sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==} - cpu: [arm64] - os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.34.6': resolution: {integrity: sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.28.1': - resolution: {integrity: sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==} - cpu: [x64] - os: [freebsd] - '@rollup/rollup-freebsd-x64@4.34.6': resolution: {integrity: sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.28.1': - resolution: {integrity: sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==} - cpu: [arm] - os: [linux] - '@rollup/rollup-linux-arm-gnueabihf@4.34.6': resolution: {integrity: sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.28.1': - resolution: {integrity: sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==} - cpu: [arm] - os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.34.6': resolution: {integrity: sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.28.1': - resolution: {integrity: sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==} - cpu: [arm64] - os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.34.6': resolution: {integrity: sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.28.1': - resolution: {integrity: sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==} - cpu: [arm64] - os: [linux] - '@rollup/rollup-linux-arm64-musl@4.34.6': resolution: {integrity: sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.28.1': - resolution: {integrity: sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==} - cpu: [loong64] - os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.34.6': resolution: {integrity: sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.28.1': - resolution: {integrity: sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==} - cpu: [ppc64] - os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.34.6': resolution: {integrity: sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.28.1': - resolution: {integrity: sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==} - cpu: [riscv64] - os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.34.6': resolution: {integrity: sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.28.1': - resolution: {integrity: sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==} - cpu: [s390x] - os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.34.6': resolution: {integrity: sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.28.1': - resolution: {integrity: sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==} - cpu: [x64] - os: [linux] - '@rollup/rollup-linux-x64-gnu@4.34.6': resolution: {integrity: sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.28.1': - resolution: {integrity: sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==} - cpu: [x64] - os: [linux] - '@rollup/rollup-linux-x64-musl@4.34.6': resolution: {integrity: sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.28.1': - resolution: {integrity: sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==} - cpu: [arm64] - os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.34.6': resolution: {integrity: sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.28.1': - resolution: {integrity: sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==} - cpu: [ia32] - os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.34.6': resolution: {integrity: sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.28.1': - resolution: {integrity: sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==} - cpu: [x64] - os: [win32] - '@rollup/rollup-win32-x64-msvc@4.34.6': resolution: {integrity: sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==} cpu: [x64] @@ -12018,11 +11923,6 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true - rollup@4.28.1: - resolution: {integrity: sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - rollup@4.34.6: resolution: {integrity: sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -13467,8 +13367,8 @@ packages: terser: optional: true - vite@6.0.3: - resolution: {integrity: sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==} + vite@6.0.8: + resolution: {integrity: sha512-rJmB+6m3Qmo5nssFmm6hbSvaCS+5tH/iuTJYeHEOHMwqu/DPrjjBs1rlecCo4D0qy5xq506hMpkKx6pKaudUxA==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -21397,117 +21297,60 @@ snapshots: estree-walker: 2.0.2 picomatch: 2.3.1 - '@rollup/rollup-android-arm-eabi@4.28.1': - optional: true - '@rollup/rollup-android-arm-eabi@4.34.6': optional: true - '@rollup/rollup-android-arm64@4.28.1': - optional: true - '@rollup/rollup-android-arm64@4.34.6': optional: true - '@rollup/rollup-darwin-arm64@4.28.1': - optional: true - '@rollup/rollup-darwin-arm64@4.34.6': optional: true - '@rollup/rollup-darwin-x64@4.28.1': - optional: true - '@rollup/rollup-darwin-x64@4.34.6': optional: true - '@rollup/rollup-freebsd-arm64@4.28.1': - optional: true - '@rollup/rollup-freebsd-arm64@4.34.6': optional: true - '@rollup/rollup-freebsd-x64@4.28.1': - optional: true - '@rollup/rollup-freebsd-x64@4.34.6': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.28.1': - optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.34.6': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.28.1': - optional: true - '@rollup/rollup-linux-arm-musleabihf@4.34.6': optional: true - '@rollup/rollup-linux-arm64-gnu@4.28.1': - optional: true - '@rollup/rollup-linux-arm64-gnu@4.34.6': optional: true - '@rollup/rollup-linux-arm64-musl@4.28.1': - optional: true - '@rollup/rollup-linux-arm64-musl@4.34.6': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.28.1': - optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.34.6': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.28.1': - optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.34.6': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.28.1': - optional: true - '@rollup/rollup-linux-riscv64-gnu@4.34.6': optional: true - '@rollup/rollup-linux-s390x-gnu@4.28.1': - optional: true - '@rollup/rollup-linux-s390x-gnu@4.34.6': optional: true - '@rollup/rollup-linux-x64-gnu@4.28.1': - optional: true - '@rollup/rollup-linux-x64-gnu@4.34.6': optional: true - '@rollup/rollup-linux-x64-musl@4.28.1': - optional: true - '@rollup/rollup-linux-x64-musl@4.34.6': optional: true - '@rollup/rollup-win32-arm64-msvc@4.28.1': - optional: true - '@rollup/rollup-win32-arm64-msvc@4.34.6': optional: true - '@rollup/rollup-win32-ia32-msvc@4.28.1': - optional: true - '@rollup/rollup-win32-ia32-msvc@4.34.6': optional: true - '@rollup/rollup-win32-x64-msvc@4.28.1': - optional: true - '@rollup/rollup-win32-x64-msvc@4.34.6': optional: true @@ -23570,14 +23413,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@4.2.1(vite@6.0.3(@types/node@22.10.1)(jiti@2.3.3)(terser@5.37.0)(yaml@2.6.1))': + '@vitejs/plugin-react@4.2.1(vite@6.0.8(@types/node@22.10.1)(jiti@2.3.3)(terser@5.37.0)(yaml@2.6.1))': dependencies: '@babel/core': 7.24.0 '@babel/plugin-transform-react-jsx-self': 7.23.3(@babel/core@7.24.0) '@babel/plugin-transform-react-jsx-source': 7.23.3(@babel/core@7.24.0) '@types/babel__core': 7.20.5 react-refresh: 0.14.0 - vite: 6.0.3(@types/node@22.10.1)(jiti@2.3.3)(terser@5.37.0)(yaml@2.6.1) + vite: 6.0.8(@types/node@22.10.1)(jiti@2.3.3)(terser@5.37.0)(yaml@2.6.1) transitivePeerDependencies: - supports-color @@ -32344,31 +32187,6 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - rollup@4.28.1: - dependencies: - '@types/estree': 1.0.6 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.28.1 - '@rollup/rollup-android-arm64': 4.28.1 - '@rollup/rollup-darwin-arm64': 4.28.1 - '@rollup/rollup-darwin-x64': 4.28.1 - '@rollup/rollup-freebsd-arm64': 4.28.1 - '@rollup/rollup-freebsd-x64': 4.28.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.28.1 - '@rollup/rollup-linux-arm-musleabihf': 4.28.1 - '@rollup/rollup-linux-arm64-gnu': 4.28.1 - '@rollup/rollup-linux-arm64-musl': 4.28.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.28.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.28.1 - '@rollup/rollup-linux-riscv64-gnu': 4.28.1 - '@rollup/rollup-linux-s390x-gnu': 4.28.1 - '@rollup/rollup-linux-x64-gnu': 4.28.1 - '@rollup/rollup-linux-x64-musl': 4.28.1 - '@rollup/rollup-win32-arm64-msvc': 4.28.1 - '@rollup/rollup-win32-ia32-msvc': 4.28.1 - '@rollup/rollup-win32-x64-msvc': 4.28.1 - fsevents: 2.3.3 - rollup@4.34.6: dependencies: '@types/estree': 1.0.6 @@ -33892,7 +33710,7 @@ snapshots: vite@2.9.18: dependencies: esbuild: 0.14.54 - postcss: 8.4.41 + postcss: 8.5.2 resolve: 1.22.8 rollup: 2.77.3 optionalDependencies: @@ -33908,11 +33726,11 @@ snapshots: fsevents: 2.3.3 terser: 5.37.0 - vite@6.0.3(@types/node@22.10.1)(jiti@2.3.3)(terser@5.37.0)(yaml@2.6.1): + vite@6.0.8(@types/node@22.10.1)(jiti@2.3.3)(terser@5.37.0)(yaml@2.6.1): dependencies: - esbuild: 0.24.0 - postcss: 8.4.49 - rollup: 4.28.1 + esbuild: 0.24.2 + postcss: 8.5.2 + rollup: 4.34.6 optionalDependencies: '@types/node': 22.10.1 fsevents: 2.3.3