From b034d5039f3261c5c3e75c5f920312b59cfceb0f Mon Sep 17 00:00:00 2001 From: lelemm Date: Tue, 7 Oct 2025 12:14:32 -0300 Subject: [PATCH] Frontend plugins Support [2/10]: Plugin service worker (#5784) * Plugin service worker --- .gitignore | 2 + bin/package-browser | 1 + bin/package-electron | 1 + eslint.config.mjs | 2 + package.json | 6 +- packages/desktop-client/.gitignore | 3 + packages/desktop-client/bin/build-browser | 1 + packages/desktop-client/bin/watch-browser | 1 + .../tsconfig.service-worker.json | 18 ++ packages/desktop-client/vite.config.mts | 34 +++- .../plugins-service/bin/build-service-worker | 28 +++ packages/plugins-service/package.json | 22 +++ .../src/plugin-service-worker.ts | 159 ++++++++++++++++++ packages/plugins-service/tsconfig.json | 17 ++ packages/plugins-service/vite.config.ts | 40 +++++ packages/sync-server/docker/alpine.Dockerfile | 1 + packages/sync-server/docker/ubuntu.Dockerfile | 1 + sync-server.Dockerfile | 1 + tsconfig.json | 3 +- upcoming-release-notes/5784.md | 7 + yarn.lock | 91 +++++++++- 21 files changed, 429 insertions(+), 10 deletions(-) create mode 100644 packages/desktop-client/tsconfig.service-worker.json create mode 100755 packages/plugins-service/bin/build-service-worker create mode 100644 packages/plugins-service/package.json create mode 100644 packages/plugins-service/src/plugin-service-worker.ts create mode 100644 packages/plugins-service/tsconfig.json create mode 100644 packages/plugins-service/vite.config.ts create mode 100644 upcoming-release-notes/5784.md diff --git a/.gitignore b/.gitignore index 06ff8833d8..2b74da590d 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,8 @@ packages/desktop-electron/build packages/desktop-electron/.electron-symbols packages/desktop-electron/dist packages/desktop-electron/loot-core +packages/desktop-client/service-worker +packages/plugins-service/dist bundle.desktop.js bundle.desktop.js.map bundle.mobile.js diff --git a/bin/package-browser b/bin/package-browser index ddc93c0bfd..526393a139 100755 --- a/bin/package-browser +++ b/bin/package-browser @@ -16,6 +16,7 @@ packages/desktop-client/bin/remove-untranslated-languages export NODE_OPTIONS="--max-old-space-size=4096" +yarn workspace plugins-service build yarn workspace loot-core build:browser yarn workspace @actual-app/web build:browser diff --git a/bin/package-electron b/bin/package-electron index c37b79bf77..ff56e9336c 100755 --- a/bin/package-electron +++ b/bin/package-electron @@ -41,6 +41,7 @@ packages/desktop-client/bin/remove-untranslated-languages export NODE_OPTIONS="--max-old-space-size=4096" +yarn workspace plugins-service build yarn workspace loot-core build:node yarn workspace @actual-app/web build --mode=desktop # electron specific build diff --git a/eslint.config.mjs b/eslint.config.mjs index 223d854041..c1562c7a90 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -83,6 +83,7 @@ export default pluginTypescript.config( 'packages/component-library/src/icons/**/*', 'packages/desktop-client/bundle.browser.js', 'packages/desktop-client/build/', + 'packages/desktop-client/service-worker/*', 'packages/desktop-client/build-electron/', 'packages/desktop-client/build-stats/', 'packages/desktop-client/public/kcab/', @@ -98,6 +99,7 @@ export default pluginTypescript.config( 'packages/loot-core/**/lib-dist/*', 'packages/loot-core/**/proto/*', 'packages/sync-server/build/', + 'packages/plugins-service/dist/', '.yarn/*', '.github/*', ], diff --git a/package.json b/package.json index 2b8bca71d3..2bab1fde7c 100644 --- a/package.json +++ b/package.json @@ -23,18 +23,20 @@ "start:server-monitor": "yarn workspace @actual-app/sync-server start-monitor", "start:server-dev": "NODE_ENV=development BROWSER_OPEN=localhost:5006 yarn npm-run-all --parallel 'start:server-monitor' 'start'", "start:desktop": "yarn desktop-dependencies && npm-run-all --parallel 'start:desktop-*'", - "desktop-dependencies": "npm-run-all --parallel rebuild-electron build:browser-backend", + "desktop-dependencies": "npm-run-all --parallel rebuild-electron build:browser-backend build:plugins-service", "start:desktop-node": "yarn workspace loot-core watch:node", "start:desktop-client": "yarn workspace @actual-app/web watch", "start:desktop-server-client": "yarn workspace @actual-app/web build:browser", "start:desktop-electron": "yarn workspace desktop-electron watch", - "start:browser": "npm-run-all --parallel 'start:browser-*'", + "start:browser": "yarn workspace plugins-service build-dev && npm-run-all --parallel 'start:browser-*'", + "start:service-plugins": "yarn workspace plugins-service watch", "start:browser-backend": "yarn workspace loot-core watch:browser", "start:browser-frontend": "yarn workspace @actual-app/web start:browser", "build:browser-backend": "yarn workspace loot-core build:browser", "build:server": "yarn build:browser && yarn workspace @actual-app/sync-server build", "build:browser": "./bin/package-browser", "build:desktop": "./bin/package-electron", + "build:plugins-service": "yarn workspace plugins-service build", "build:api": "yarn workspace @actual-app/api build", "generate:i18n": "yarn workspace @actual-app/web generate:i18n", "generate:release-notes": "ts-node ./bin/release-note-generator.ts", diff --git a/packages/desktop-client/.gitignore b/packages/desktop-client/.gitignore index a67335f517..eb9b164f0d 100644 --- a/packages/desktop-client/.gitignore +++ b/packages/desktop-client/.gitignore @@ -14,6 +14,9 @@ build-electron build-stats stats.json +# generated service worker +service-worker/ + # misc .DS_Store .env diff --git a/packages/desktop-client/bin/build-browser b/packages/desktop-client/bin/build-browser index 3e6e18aa6c..f53151b442 100755 --- a/packages/desktop-client/bin/build-browser +++ b/packages/desktop-client/bin/build-browser @@ -9,6 +9,7 @@ rm -fr build export IS_GENERIC_BROWSER=1 export REACT_APP_BACKEND_WORKER_HASH=`ls "$ROOT"/../public/kcab/kcab.worker.*.js | sed 's/.*kcab\.worker\.\(.*\)\.js/\1/'` +export REACT_APP_PLUGIN_SERVICE_WORKER_HASH=`ls "$ROOT"/../service-worker/plugin-sw.*.js | sed 's/.*plugin-sw\.\(.*\)\.js/\1/'` yarn build diff --git a/packages/desktop-client/bin/watch-browser b/packages/desktop-client/bin/watch-browser index 2f2861696f..d90eba99c1 100755 --- a/packages/desktop-client/bin/watch-browser +++ b/packages/desktop-client/bin/watch-browser @@ -6,5 +6,6 @@ cd "$ROOT/.." export IS_GENERIC_BROWSER=1 export PORT=3001 export REACT_APP_BACKEND_WORKER_HASH="dev" +export REACT_APP_PLUGIN_SERVICE_WORKER_HASH="dev" yarn start diff --git a/packages/desktop-client/tsconfig.service-worker.json b/packages/desktop-client/tsconfig.service-worker.json new file mode 100644 index 0000000000..56ebc5b10a --- /dev/null +++ b/packages/desktop-client/tsconfig.service-worker.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "./service-worker", + "target": "ES2022", + "lib": ["ES2022", "WebWorker", "DOM", "DOM.Iterable"], + "module": "ES2022", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "skipLibCheck": true, + "strict": false, + "types": ["vite/client"] + }, + "include": ["src/plugin-service-worker.ts"], + "exclude": ["**/*.test.ts", "**/*.spec.ts"] +} diff --git a/packages/desktop-client/vite.config.mts b/packages/desktop-client/vite.config.mts index 719aaf4df3..ca117fb285 100644 --- a/packages/desktop-client/vite.config.mts +++ b/packages/desktop-client/vite.config.mts @@ -26,6 +26,15 @@ const addWatchers = (): Plugin => ({ }, }); +// Get service worker filename from environment variable +function getServiceWorkerFilename(): string { + const hash = process.env.REACT_APP_PLUGIN_SERVICE_WORKER_HASH; + if (hash) { + return `plugin-sw.${hash}.js`; + } + return 'plugin-sw.js'; // fallback +} + // Inject build shims using the inject plugin const injectShims = (): Plugin[] => { const buildShims = path.resolve('./src/build-shims.js'); @@ -159,18 +168,41 @@ export default defineConfig(async ({ mode }) => { ? undefined : VitePWA({ registerType: 'prompt', + strategies: 'injectManifest', + srcDir: 'service-worker', + filename: getServiceWorkerFilename(), + manifest: { + name: 'Actual', + short_name: 'Actual', + description: 'A local-first personal finance tool', + theme_color: '#8812E1', + background_color: '#8812E1', + display: 'standalone', + start_url: './', + }, + injectManifest: { + maximumFileSizeToCacheInBytes: 10 * 1024 * 1024, // 10MB + swSrc: `service-worker/${getServiceWorkerFilename()}`, + }, + devOptions: { + enabled: true, // We need service worker in dev mode to work with plugins + type: 'module', + }, workbox: { globPatterns: [ '**/*.{js,css,html,txt,wasm,sql,sqlite,ico,png,woff2,webmanifest}', ], ignoreURLParametersMatching: [/^v$/], navigateFallback: '/index.html', - maximumFileSizeToCacheInBytes: 5 * 1024 * 1024, // 5MB + maximumFileSizeToCacheInBytes: 10 * 1024 * 1024, // 10MB navigateFallbackDenylist: [ /^\/account\/.*$/, /^\/admin\/.*$/, /^\/secret\/.*$/, /^\/openid\/.*$/, + /^\/plugins\/.*$/, + /^\/kcab\/.*$/, + /^\/plugin-data\/.*$/, ], }, }), diff --git a/packages/plugins-service/bin/build-service-worker b/packages/plugins-service/bin/build-service-worker new file mode 100755 index 0000000000..813463f3f7 --- /dev/null +++ b/packages/plugins-service/bin/build-service-worker @@ -0,0 +1,28 @@ +#!/bin/bash -e + +cd `dirname "$0"` +ROOT=`pwd -P` +VITE_ARGS="" + +DESKTOP_DIR="$ROOT"/../../desktop-client +SERVICE_WORKER_DIR="$DESKTOP_DIR"/service-worker +mkdir -p "$SERVICE_WORKER_DIR" + +# Clean out previous build files +rm -f ../dist/* +rm -rf "$DESKTOP_DIR"/service-worker/* + +if [ $NODE_ENV == 'development' ]; then + if [ "$OSTYPE" == "msys" ]; then + # Ensure symlinks are created as native Windows symlinks. + export MSYS=winsymlinks:nativestrict + fi + ln -snf "$ROOT"/../dist "$DESKTOP_DIR"/service-worker +fi + +yarn vite build --config ../vite.config.ts --mode $NODE_ENV $VITE_ARGS + +if [ $NODE_ENV == 'production' ]; then + # In production, just copy the built files + cp -r ../dist/* "$DESKTOP_DIR"/service-worker +fi \ No newline at end of file diff --git a/packages/plugins-service/package.json b/packages/plugins-service/package.json new file mode 100644 index 0000000000..f32ed44b27 --- /dev/null +++ b/packages/plugins-service/package.json @@ -0,0 +1,22 @@ +{ + "name": "plugins-service", + "version": "0.0.1", + "description": "Plugin service worker for Actual", + "main": "plugin-sw.js", + "scripts": { + "build": "cross-env NODE_ENV=production ./bin/build-service-worker", + "build-dev": "cross-env NODE_ENV=development ./bin/build-service-worker", + "watch": "cross-env NODE_ENV=development ./bin/build-service-worker --watch" + }, + "author": "", + "license": "ISC", + "dependencies": { + "workbox-precaching": "^7.0.0" + }, + "devDependencies": { + "@types/node": "^22.17.0", + "cross-env": "^7.0.3", + "typescript": "^5.9.2", + "vite": "^6.3.6" + } +} diff --git a/packages/plugins-service/src/plugin-service-worker.ts b/packages/plugins-service/src/plugin-service-worker.ts new file mode 100644 index 0000000000..02d116ddff --- /dev/null +++ b/packages/plugins-service/src/plugin-service-worker.ts @@ -0,0 +1,159 @@ +/// +import { precacheAndRoute } from 'workbox-precaching'; + +// Service Worker Global Types +declare const self: ServiceWorkerGlobalScope & { + __WB_DISABLE_DEV_LOGS: boolean; +}; + +type PluginFile = { + name: string; + content: string; +}; + +type PluginMessage = { + type: string; + eventData?: { + pluginUrl: string; + }; +}; + +self.__WB_DISABLE_DEV_LOGS = true; + +// Injected by VitePWA +precacheAndRoute(self.__WB_MANIFEST); + +const fileList = new Map(); + +// Log installation event +self.addEventListener('install', (_event: ExtendableEvent) => { + console.log('Plugins Worker installing...'); + self.skipWaiting(); // Forces activation immediately +}); + +// Log activation event +self.addEventListener('activate', (_event: ExtendableEvent) => { + self.clients.claim(); + + self.clients.matchAll().then(clients => { + clients.forEach(client => { + client.postMessage({ + type: 'service-worker-ready', + timestamp: Date.now(), + }); + }); + }); +}); + +self.addEventListener('message', (event: ExtendableMessageEvent) => { + if (event.data && (event.data as PluginMessage).type === 'SKIP_WAITING') { + self.skipWaiting(); + } +}); + +self.addEventListener('fetch', (event: FetchEvent) => { + const url = new URL(event.request.url); + const pathSegments = url.pathname.split('/').filter(Boolean); // Split and remove empty segments + + const pluginsIndex = pathSegments.indexOf('plugin-data'); + const slugIndex = pluginsIndex + 1; + if (pluginsIndex !== -1 && pathSegments[slugIndex]) { + const slug = pathSegments[slugIndex]; + const fileName = + pathSegments.length > slugIndex + 1 + ? pathSegments[slugIndex + 1].split('?')[0] + : ''; + event.respondWith(handlePlugin(slug, fileName.replace('?import', ''))); + } +}); + +async function handlePlugin(slug: string, fileName: string): Promise { + for (const key of fileList.keys()) { + if (key.startsWith(`${slug}/`)) { + if (key.endsWith(`/${fileName}`)) { + const content = fileList.get(key); + const contentType = getContentType(fileName); + return new Response(content, { + headers: { 'Content-Type': contentType }, + }); + } + } + } + + const clientsList = await self.clients.matchAll(); + if (clientsList.length === 0) { + return new Response( + JSON.stringify({ error: 'No active clients to process' }), + { + status: 404, + headers: { 'Content-Type': 'application/json' }, + }, + ); + } + + const client = clientsList[0]; + return new Promise(resolve => { + const channel = new MessageChannel(); + channel.port1.onmessage = (messageEvent: MessageEvent) => { + const responseData = messageEvent.data as PluginFile[]; + + if (responseData && Array.isArray(responseData)) { + responseData.forEach(({ name, content }) => { + fileList.set(`${slug}/${encodeURIComponent(name)}`, content); + }); + } + + const fileToCheck = fileName.length > 0 ? fileName : 'mf-manifest.json'; + + if (fileList.has(`${slug}/${fileToCheck}`)) { + let content = fileList.get(`${slug}/${fileToCheck}`)!; + const contentType = getContentType(fileToCheck); + const headers: Record = { 'Content-Type': contentType }; + + if (fileToCheck === 'mf-manifest.json') { + try { + const manifest = JSON.parse(content); + if (manifest.metaData?.publicPath) { + manifest.metaData.publicPath = `/plugin-data/${slug}/`; + content = JSON.stringify(manifest); + } + } catch (error) { + console.error( + 'Failed to parse manifest for publicPath rewrite:', + error, + ); + } + + headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'; + headers['Pragma'] = 'no-cache'; + headers['Expires'] = '0'; + } + + resolve(new Response(content, { headers })); + } else { + resolve(new Response('File not found', { status: 404 })); + } + }; + + client.postMessage( + { type: 'plugin-files', eventData: { pluginUrl: slug } }, + [channel.port2], + ); + }); +} + +function getContentType(fileName: string): string { + const extension = fileName.split('.').pop()?.toLowerCase() || ''; + const mimeTypes: Record = { + html: 'text/html', + css: 'text/css', + js: 'application/javascript', + json: 'application/json', + png: 'image/png', + jpg: 'image/jpeg', + jpeg: 'image/jpeg', + gif: 'image/gif', + svg: 'image/svg+xml', + }; + return mimeTypes[extension] || 'application/octet-stream'; +} diff --git a/packages/plugins-service/tsconfig.json b/packages/plugins-service/tsconfig.json new file mode 100644 index 0000000000..fab078e6e6 --- /dev/null +++ b/packages/plugins-service/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022", "WebWorker", "DOM", "DOM.Iterable"], + "module": "ES2022", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "skipLibCheck": true, + "strict": false, + "types": ["vite/client"], + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/packages/plugins-service/vite.config.ts b/packages/plugins-service/vite.config.ts new file mode 100644 index 0000000000..1d32177b34 --- /dev/null +++ b/packages/plugins-service/vite.config.ts @@ -0,0 +1,40 @@ +// @ts-strict-ignore +import path from 'path'; + +import { defineConfig } from 'vite'; + +// eslint-disable-next-line import/no-default-export +export default defineConfig(({ mode }) => { + const isDev = mode === 'development'; + const outDir = path.resolve(__dirname, 'dist'); + + return { + mode, + build: { + target: 'es2020', + outDir, + emptyOutDir: true, + lib: { + entry: path.resolve(__dirname, 'src/plugin-service-worker.ts'), + name: 'plugin_sw', + formats: ['iife'], + fileName: () => (isDev ? 'plugin-sw.dev.js' : 'plugin-sw.[hash].js'), + }, + sourcemap: true, + minify: isDev ? false : 'terser', + terserOptions: { + compress: { + drop_debugger: false, + }, + mangle: false, + }, + }, + resolve: { + extensions: ['.js', '.ts', '.json'], + }, + define: { + 'process.env': '{}', + 'process.env.IS_DEV': JSON.stringify(isDev), + }, + }; +}); diff --git a/packages/sync-server/docker/alpine.Dockerfile b/packages/sync-server/docker/alpine.Dockerfile index 72b9f4fa35..c3ce965260 100644 --- a/packages/sync-server/docker/alpine.Dockerfile +++ b/packages/sync-server/docker/alpine.Dockerfile @@ -16,6 +16,7 @@ COPY packages/desktop-electron/package.json packages/desktop-electron/package.js COPY packages/eslint-plugin-actual/package.json packages/eslint-plugin-actual/package.json COPY packages/loot-core/package.json packages/loot-core/package.json COPY packages/sync-server/package.json packages/sync-server/package.json +COPY packages/plugins-service/package.json packages/plugins-service/package.json # Avoiding memory issues with ARMv7 RUN if [ "$(uname -m)" = "armv7l" ]; then yarn config set taskPoolConcurrency 2; yarn config set networkConcurrency 5; fi diff --git a/packages/sync-server/docker/ubuntu.Dockerfile b/packages/sync-server/docker/ubuntu.Dockerfile index 5759eb42c3..c84a4ba7fd 100644 --- a/packages/sync-server/docker/ubuntu.Dockerfile +++ b/packages/sync-server/docker/ubuntu.Dockerfile @@ -16,6 +16,7 @@ COPY packages/desktop-electron/package.json packages/desktop-electron/package.js COPY packages/eslint-plugin-actual/package.json packages/eslint-plugin-actual/package.json COPY packages/loot-core/package.json packages/loot-core/package.json COPY packages/sync-server/package.json packages/sync-server/package.json +COPY packages/plugins-service/package.json packages/plugins-service/package.json # Avoiding memory issues with ARMv7 RUN if [ "$(uname -m)" = "armv7l" ]; then yarn config set taskPoolConcurrency 2; yarn config set networkConcurrency 5; fi diff --git a/sync-server.Dockerfile b/sync-server.Dockerfile index 1175cd555b..c8879c6569 100644 --- a/sync-server.Dockerfile +++ b/sync-server.Dockerfile @@ -16,6 +16,7 @@ COPY packages/desktop-electron/package.json packages/desktop-electron/package.js COPY packages/eslint-plugin-actual/package.json packages/eslint-plugin-actual/package.json COPY packages/loot-core/package.json packages/loot-core/package.json COPY packages/sync-server/package.json packages/sync-server/package.json +COPY packages/plugins-service/package.json packages/plugins-service/package.json COPY ./bin/package-browser ./bin/package-browser diff --git a/tsconfig.json b/tsconfig.json index bd1112bbbd..32f514cb1d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -52,7 +52,8 @@ "**/dist/*", "**/lib-dist/*", "**/test-results/*", - "**/playwright-report/*" + "**/playwright-report/*", + "**/service-worker/*" ], "ts-node": { "compilerOptions": { diff --git a/upcoming-release-notes/5784.md b/upcoming-release-notes/5784.md new file mode 100644 index 0000000000..85b9b43c03 --- /dev/null +++ b/upcoming-release-notes/5784.md @@ -0,0 +1,7 @@ +--- +category: Features +authors: [lelemm] +--- + +Introduce a Workbox-based service worker for enhanced plugin support and caching functionality. + diff --git a/yarn.lock b/yarn.lock index c526d16c52..e8d448071d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6717,7 +6717,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^22.18.8": +"@types/node@npm:^22.17.0, @types/node@npm:^22.18.8": version: 22.18.8 resolution: "@types/node@npm:22.18.8" dependencies: @@ -9479,6 +9479,18 @@ __metadata: languageName: node linkType: hard +"cross-env@npm:^7.0.3": + version: 7.0.3 + resolution: "cross-env@npm:7.0.3" + dependencies: + cross-spawn: "npm:^7.0.1" + bin: + cross-env: src/bin/cross-env.js + cross-env-shell: src/bin/cross-env-shell.js + checksum: 10/e99911f0d31c20e990fd92d6fd001f4b01668a303221227cc5cb42ed155f086351b1b3bd2699b200e527ab13011b032801f8ce638e6f09f854bdf744095e604c + languageName: node + linkType: hard + "cross-spawn@npm:^6.0.5": version: 6.0.5 resolution: "cross-spawn@npm:6.0.5" @@ -16670,6 +16682,18 @@ __metadata: languageName: node linkType: hard +"plugins-service@workspace:packages/plugins-service": + version: 0.0.0-use.local + resolution: "plugins-service@workspace:packages/plugins-service" + dependencies: + "@types/node": "npm:^22.17.0" + cross-env: "npm:^7.0.3" + typescript: "npm:^5.9.2" + vite: "npm:^6.3.6" + workbox-precaching: "npm:^7.0.0" + languageName: unknown + linkType: soft + "possible-typed-array-names@npm:^1.0.0": version: 1.0.0 resolution: "possible-typed-array-names@npm:1.0.0" @@ -16677,7 +16701,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.5.5, postcss@npm:^8.5.6": +"postcss@npm:^8.5.3, postcss@npm:^8.5.5, postcss@npm:^8.5.6": version: 8.5.6 resolution: "postcss@npm:8.5.6" dependencies: @@ -19661,7 +19685,7 @@ __metadata: languageName: node linkType: hard -"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.15": +"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.13, tinyglobby@npm:^0.2.15": version: 0.2.15 resolution: "tinyglobby@npm:0.2.15" dependencies: @@ -20144,7 +20168,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.9.3": +"typescript@npm:^5.9.2, typescript@npm:^5.9.3": version: 5.9.3 resolution: "typescript@npm:5.9.3" bin: @@ -20174,7 +20198,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.9.3#optional!builtin": +"typescript@patch:typescript@npm%3A^5.9.2#optional!builtin, typescript@patch:typescript@npm%3A^5.9.3#optional!builtin": version: 5.9.3 resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" bin: @@ -20940,6 +20964,61 @@ __metadata: languageName: node linkType: hard +"vite@npm:^6.3.6": + version: 6.3.6 + resolution: "vite@npm:6.3.6" + dependencies: + esbuild: "npm:^0.25.0" + fdir: "npm:^6.4.4" + fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.2" + postcss: "npm:^8.5.3" + rollup: "npm:^4.34.9" + tinyglobby: "npm:^0.2.13" + peerDependencies: + "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: ">=1.21.0" + less: "*" + lightningcss: ^1.21.0 + sass: "*" + sass-embedded: "*" + stylus: "*" + sugarss: "*" + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10/8b8b6fe12318ca457396bf2053df7056cf4810f1d4a43b36b6afe59860e32b749c0685a290fe8a973b0d3da179ceec4c30cebbd3c91d0c47fbcf6436b17bdeef + languageName: node + linkType: hard + "vite@npm:^7.1.9": version: 7.1.9 resolution: "vite@npm:7.1.9" @@ -21460,7 +21539,7 @@ __metadata: languageName: node linkType: hard -"workbox-precaching@npm:7.3.0": +"workbox-precaching@npm:7.3.0, workbox-precaching@npm:^7.0.0": version: 7.3.0 resolution: "workbox-precaching@npm:7.3.0" dependencies: