From 08473b623048df656558618821967dbbc51347ec Mon Sep 17 00:00:00 2001 From: zhsama Date: Tue, 11 Aug 2026 06:37:23 +0800 Subject: [PATCH] feat: add Vue Vapor benchmark --- .github/docs/apps.md | 3 +- apps/vue-vapor/.npmrc | 3 + apps/vue-vapor/README.md | 80 + apps/vue-vapor/index.html | 16 + apps/vue-vapor/package-lock.json | 1368 +++++++++++++++++ apps/vue-vapor/package.json | 29 + apps/vue-vapor/src/App.vue | 93 ++ .../src/components/CurrentWeather.vue | 102 ++ apps/vue-vapor/src/components/ErrorState.vue | 15 + apps/vue-vapor/src/components/Forecast.vue | 51 + .../vue-vapor/src/components/ForecastItem.vue | 84 + .../vue-vapor/src/components/LoadingState.vue | 10 + apps/vue-vapor/src/components/SearchForm.vue | 66 + .../src/components/WeatherContent.vue | 20 + apps/vue-vapor/src/main.js | 5 + apps/vue-vapor/src/services/weatherService.js | 196 +++ apps/vue-vapor/src/utils/weatherUtils.js | 121 ++ apps/vue-vapor/vite.config.js | 12 + frameworks.json | 30 + package.json | 12 +- tests/config/playwright-vue-vapor.config.js | 3 + tests/config/playwright.config.base.js | 7 + 22 files changed, 2321 insertions(+), 5 deletions(-) create mode 100644 apps/vue-vapor/.npmrc create mode 100644 apps/vue-vapor/README.md create mode 100644 apps/vue-vapor/index.html create mode 100644 apps/vue-vapor/package-lock.json create mode 100644 apps/vue-vapor/package.json create mode 100644 apps/vue-vapor/src/App.vue create mode 100644 apps/vue-vapor/src/components/CurrentWeather.vue create mode 100644 apps/vue-vapor/src/components/ErrorState.vue create mode 100644 apps/vue-vapor/src/components/Forecast.vue create mode 100644 apps/vue-vapor/src/components/ForecastItem.vue create mode 100644 apps/vue-vapor/src/components/LoadingState.vue create mode 100644 apps/vue-vapor/src/components/SearchForm.vue create mode 100644 apps/vue-vapor/src/components/WeatherContent.vue create mode 100644 apps/vue-vapor/src/main.js create mode 100644 apps/vue-vapor/src/services/weatherService.js create mode 100644 apps/vue-vapor/src/utils/weatherUtils.js create mode 100644 apps/vue-vapor/vite.config.js create mode 100644 tests/config/playwright-vue-vapor.config.js diff --git a/.github/docs/apps.md b/.github/docs/apps.md index 7dc35e72f..1c63ae2c3 100644 --- a/.github/docs/apps.md +++ b/.github/docs/apps.md @@ -1,6 +1,6 @@ # Framework Applications -This project contains the same weather application built using 15 different JavaScript frameworks. Each implementation provides identical functionality but uses the specific patterns and approaches of its framework. +This project contains the same weather application built using 16 different JavaScript frameworks. Each implementation provides identical functionality but uses the specific patterns and approaches of its framework. ## Available Frameworks @@ -8,6 +8,7 @@ The project includes implementations for: **React** - Component-based with hooks and modern React patterns **Vue** - Built with Vue 3 and the Composition API +**Vue Vapor** - Vue 3.6's opt-in compiler mode with direct DOM updates **Svelte** - Compiled framework with reactive statements **Angular** - Full TypeScript application with services and components **Qwik** - Resumable framework optimized for performance diff --git a/apps/vue-vapor/.npmrc b/apps/vue-vapor/.npmrc new file mode 100644 index 000000000..f105bb4e2 --- /dev/null +++ b/apps/vue-vapor/.npmrc @@ -0,0 +1,3 @@ +# Vue 3.6 is still an RC, while plugin-vue's compatible peer range excludes +# prerelease versions under npm's strict semver rules. +legacy-peer-deps=true diff --git a/apps/vue-vapor/README.md b/apps/vue-vapor/README.md new file mode 100644 index 000000000..83958835b --- /dev/null +++ b/apps/vue-vapor/README.md @@ -0,0 +1,80 @@ + +

💨 Weather Front - Vue Vapor Mode

+ +

+
+ A tiny weather app +
+ 🚀 Demo📊 Results +

+ + + +

+ + + + +## About + + + +This is a simple weather app, built in [Vue Vapor Mode](https://github.com/vuejs/core/releases/tag/v3.6.0-rc.2) (as well as also [10 other frontend frameworks](/)) in order to review, compare and benchmark frontend web frameworks. + +- 🌦️ Live weather conditions +- 📅 7-day weather forecast +- 🔍 City search functionality +- 📍 Geolocation support +- 💾 Persistent location storage +- 📱 Responsive design +- ♿ Accessible interface +- 🎨 Multi-theme support +- 🧪 Fully unit tested +- 🌐 Internationalized + + + + + +## Status + +| Task | Status | +|---|---| +| **Test** - Executes all e2e and unit tests | [![Test Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/refs/heads/badges/test-vue-vapor.svg)](https://github.com/lissy93/framework-benchmarks/actions/workflows/test.yml) | +| **Lint** - Verifies code style and quality | [![Lint Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/refs/heads/badges/lint-vue-vapor.svg)](https://github.com/lissy93/framework-benchmarks/actions/workflows/lint.yml) | +| **Build** - Builds and deploys the app | [![Build Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/refs/heads/badges/build-vue-vapor.svg)](https://github.com/lissy93/framework-benchmarks/actions/workflows/build.yml) | + + + + + +## Usage + +First, follow the [repo setup instructions](https://github.com/lissy93/framework-benchmarks?tab=readme-ov-file#usage). Then `cd apps/vue-vapor` and use the following commands: + +```bash +npm run dev # Start dev server (vite --port 3000) +npm test # Run tests +npm run lint # Run lint checks +npm build # Build for production (vite build) +npm start # Serve built prod app (from ./dist) +``` + +For troubleshooting, use `npm run verify` from the root of the project. + + + + + + + + + + + +## License + +Weather-Front is licensed under [MIT](https://github.com/lissy93/framework-benchmarks/blob/main/LICENSE) © Alicia Sykes 2025.
+View [Attributions](https://github.com/lissy93/framework-benchmarks?tab=readme-ov-file#attributions) for credits, thanks and contributors. + + \ No newline at end of file diff --git a/apps/vue-vapor/index.html b/apps/vue-vapor/index.html new file mode 100644 index 000000000..e86a19e0c --- /dev/null +++ b/apps/vue-vapor/index.html @@ -0,0 +1,16 @@ + + + + + + Weather App - Vue Vapor Mode + + + + + + +
+ + + diff --git a/apps/vue-vapor/package-lock.json b/apps/vue-vapor/package-lock.json new file mode 100644 index 000000000..b15fb8eb5 --- /dev/null +++ b/apps/vue-vapor/package-lock.json @@ -0,0 +1,1368 @@ +{ + "name": "weather-app-vue-vapor", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "weather-app-vue-vapor", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "vue": "3.6.0-rc.2" + }, + "devDependencies": { + "@playwright/test": "^1.40.0", + "@vitejs/plugin-vue": "6.0.8", + "vite": "^5.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@playwright/test": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.62.1.tgz", + "integrity": "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", + "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", + "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", + "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", + "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", + "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", + "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", + "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", + "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", + "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", + "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", + "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", + "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", + "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", + "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", + "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", + "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", + "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz", + "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz", + "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", + "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", + "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", + "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", + "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", + "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", + "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz", + "integrity": "sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.6.0-rc.2", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.6.0-rc.2.tgz", + "integrity": "sha512-Xx79h4EpJktQBShelXNKaRuvn9N6YsYL1j+RXdvPKzI+8tohYziBJbxOFGAkdcOKBwKc2SIWV4DOlTAMhE96yQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/shared": "3.6.0-rc.2", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.6.0-rc.2", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.6.0-rc.2.tgz", + "integrity": "sha512-dRTdTdi/KXzHYIkHtyjGxepTIxnsZ/hB3v1osnS8c4MeycAyzkSoZW6OZkXXN4oVVVU6NKXds1XtKAEKVVw7nA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.6.0-rc.2", + "@vue/shared": "3.6.0-rc.2" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.6.0-rc.2", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.6.0-rc.2.tgz", + "integrity": "sha512-EZWTNh0UwdxvrBJkFc6+dqfwuUSdTVHDIohQcGDPCqGaYTHOfUL20b+grEAzlyxB741KrVBUb7qqpes92hAh2w==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/compiler-core": "3.6.0-rc.2", + "@vue/compiler-dom": "3.6.0-rc.2", + "@vue/compiler-ssr": "3.6.0-rc.2", + "@vue/compiler-vapor": "3.6.0-rc.2", + "@vue/shared": "3.6.0-rc.2", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.19", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.6.0-rc.2", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.6.0-rc.2.tgz", + "integrity": "sha512-lqHSdQTmlhKX3dNEvR1TcspUzDlTR82T7BYLDzfvTwAqGXxAI2WJfyGJPcNpPjPKYEMI9JRGF32DU8TlHLx1AA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.6.0-rc.2", + "@vue/shared": "3.6.0-rc.2" + } + }, + "node_modules/@vue/compiler-vapor": { + "version": "3.6.0-rc.2", + "resolved": "https://registry.npmjs.org/@vue/compiler-vapor/-/compiler-vapor-3.6.0-rc.2.tgz", + "integrity": "sha512-QbrA01pbj9xemF2McgBopyaOwj3kQGN0W1GW5EdFXORIT4fQQJpmUOj40h99S3bk09gLs5bFTRS4YE+Oyw/F9w==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/compiler-dom": "3.6.0-rc.2", + "@vue/shared": "3.6.0-rc.2", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.6.0-rc.2", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.6.0-rc.2.tgz", + "integrity": "sha512-B01EoSTcaVQiJwZDoSoeKYClVgj60BHN43oHEcxXhPVLdxsXI0SOiQuskUskQDZn2ma08llICKJohL60o+UamA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.6.0-rc.2" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.6.0-rc.2", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.6.0-rc.2.tgz", + "integrity": "sha512-qFV61Hbg6LyEGxJiVRJYuiIfryxrfO4+cgW1esxtkR1/pvemULwG6KpFImsJDwcC+YxrtvndXGM5PRVwUCGuiA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.6.0-rc.2", + "@vue/shared": "3.6.0-rc.2" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.6.0-rc.2", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.6.0-rc.2.tgz", + "integrity": "sha512-clIE/xk0b61t30BPKoOXZep253nhWdjyw7iac53yy2KN0nxAupkb6x3ImOI9iljjXqOW5lYdNVHNxNlcx3iA1Q==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.6.0-rc.2", + "@vue/runtime-core": "3.6.0-rc.2", + "@vue/shared": "3.6.0-rc.2", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/runtime-vapor": { + "version": "3.6.0-rc.2", + "resolved": "https://registry.npmjs.org/@vue/runtime-vapor/-/runtime-vapor-3.6.0-rc.2.tgz", + "integrity": "sha512-8EpxbNyT629k/52el4D7PxXmZtiYhJRBHPXJR7CZw0OluiADtT9W8J+XB83EVlzHmFUysXcd+mGWmuHwIhuHZA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.6.0-rc.2", + "@vue/shared": "3.6.0-rc.2" + }, + "peerDependencies": { + "@vue/runtime-dom": "3.6.0-rc.2" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.6.0-rc.2", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.6.0-rc.2.tgz", + "integrity": "sha512-TIsqoYljs6b0wfoCDhv4EeU3Du2a8gTadkaTGMjIYLTLlO4p8r4VdDqfNp/ZMAm87bW4ST1naBMRe+bXQBpAeQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.6.0-rc.2", + "@vue/runtime-dom": "3.6.0-rc.2", + "@vue/shared": "3.6.0-rc.2" + } + }, + "node_modules/@vue/shared": { + "version": "3.6.0-rc.2", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.6.0-rc.2.tgz", + "integrity": "sha512-Tz44lKLjxhWRu3GJ38768dc2hW4XhDtTB5aUo5fCMis6YDyFzwQYkrD/B9SuQ1v/ailv6UVR3wSHiujNXSjMCQ==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/playwright": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz", + "integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz", + "integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz", + "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.62.4", + "@rollup/rollup-android-arm64": "4.62.4", + "@rollup/rollup-darwin-arm64": "4.62.4", + "@rollup/rollup-darwin-x64": "4.62.4", + "@rollup/rollup-freebsd-arm64": "4.62.4", + "@rollup/rollup-freebsd-x64": "4.62.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", + "@rollup/rollup-linux-arm-musleabihf": "4.62.4", + "@rollup/rollup-linux-arm64-gnu": "4.62.4", + "@rollup/rollup-linux-arm64-musl": "4.62.4", + "@rollup/rollup-linux-loong64-gnu": "4.62.4", + "@rollup/rollup-linux-loong64-musl": "4.62.4", + "@rollup/rollup-linux-ppc64-gnu": "4.62.4", + "@rollup/rollup-linux-ppc64-musl": "4.62.4", + "@rollup/rollup-linux-riscv64-gnu": "4.62.4", + "@rollup/rollup-linux-riscv64-musl": "4.62.4", + "@rollup/rollup-linux-s390x-gnu": "4.62.4", + "@rollup/rollup-linux-x64-gnu": "4.62.4", + "@rollup/rollup-linux-x64-musl": "4.62.4", + "@rollup/rollup-openbsd-x64": "4.62.4", + "@rollup/rollup-openharmony-arm64": "4.62.4", + "@rollup/rollup-win32-arm64-msvc": "4.62.4", + "@rollup/rollup-win32-ia32-msvc": "4.62.4", + "@rollup/rollup-win32-x64-gnu": "4.62.4", + "@rollup/rollup-win32-x64-msvc": "4.62.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vue": { + "version": "3.6.0-rc.2", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.6.0-rc.2.tgz", + "integrity": "sha512-pBgoISqETcufJ1e92vVW2/D5MEbCm2XHlC/5dq3hWq8s17tx9v5BEdYv4hojJ0yEEDidYxUL7itS+vV32wfZrw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.6.0-rc.2", + "@vue/compiler-sfc": "3.6.0-rc.2", + "@vue/runtime-dom": "3.6.0-rc.2", + "@vue/runtime-vapor": "3.6.0-rc.2", + "@vue/server-renderer": "3.6.0-rc.2", + "@vue/shared": "3.6.0-rc.2" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + } + } +} diff --git a/apps/vue-vapor/package.json b/apps/vue-vapor/package.json new file mode 100644 index 000000000..380c49126 --- /dev/null +++ b/apps/vue-vapor/package.json @@ -0,0 +1,29 @@ +{ + "name": "weather-app-vue-vapor", + "version": "1.0.0", + "description": "Weather app built with Vue Vapor Mode", + "scripts": { + "dev": "vite --port 3000", + "build": "vite build", + "preview": "vite preview", + "test": "playwright test", + "test:ui": "playwright test --ui" + }, + "dependencies": { + "vue": "3.6.0-rc.2" + }, + "devDependencies": { + "@playwright/test": "^1.40.0", + "@vitejs/plugin-vue": "6.0.8", + "vite": "^5.0.0" + }, + "type": "module", + "keywords": [ + "weather", + "vue", + "vue-vapor", + "frontend" + ], + "author": "", + "license": "MIT" +} diff --git a/apps/vue-vapor/src/App.vue b/apps/vue-vapor/src/App.vue new file mode 100644 index 000000000..7a072428d --- /dev/null +++ b/apps/vue-vapor/src/App.vue @@ -0,0 +1,93 @@ + + + diff --git a/apps/vue-vapor/src/components/CurrentWeather.vue b/apps/vue-vapor/src/components/CurrentWeather.vue new file mode 100644 index 000000000..7fd840d1d --- /dev/null +++ b/apps/vue-vapor/src/components/CurrentWeather.vue @@ -0,0 +1,102 @@ + + + diff --git a/apps/vue-vapor/src/components/ErrorState.vue b/apps/vue-vapor/src/components/ErrorState.vue new file mode 100644 index 000000000..3b742ad22 --- /dev/null +++ b/apps/vue-vapor/src/components/ErrorState.vue @@ -0,0 +1,15 @@ + + + diff --git a/apps/vue-vapor/src/components/Forecast.vue b/apps/vue-vapor/src/components/Forecast.vue new file mode 100644 index 000000000..b32912951 --- /dev/null +++ b/apps/vue-vapor/src/components/Forecast.vue @@ -0,0 +1,51 @@ + + + diff --git a/apps/vue-vapor/src/components/ForecastItem.vue b/apps/vue-vapor/src/components/ForecastItem.vue new file mode 100644 index 000000000..b37c10a9e --- /dev/null +++ b/apps/vue-vapor/src/components/ForecastItem.vue @@ -0,0 +1,84 @@ + + + diff --git a/apps/vue-vapor/src/components/LoadingState.vue b/apps/vue-vapor/src/components/LoadingState.vue new file mode 100644 index 000000000..5914ae217 --- /dev/null +++ b/apps/vue-vapor/src/components/LoadingState.vue @@ -0,0 +1,10 @@ + + + diff --git a/apps/vue-vapor/src/components/SearchForm.vue b/apps/vue-vapor/src/components/SearchForm.vue new file mode 100644 index 000000000..92e074bd8 --- /dev/null +++ b/apps/vue-vapor/src/components/SearchForm.vue @@ -0,0 +1,66 @@ + + + diff --git a/apps/vue-vapor/src/components/WeatherContent.vue b/apps/vue-vapor/src/components/WeatherContent.vue new file mode 100644 index 000000000..58b5401ea --- /dev/null +++ b/apps/vue-vapor/src/components/WeatherContent.vue @@ -0,0 +1,20 @@ + + + diff --git a/apps/vue-vapor/src/main.js b/apps/vue-vapor/src/main.js new file mode 100644 index 000000000..36aaebeba --- /dev/null +++ b/apps/vue-vapor/src/main.js @@ -0,0 +1,5 @@ +import { createVaporApp } from 'vue'; +import App from './App.vue'; + +// Reason: a pure Vapor root avoids pulling the Virtual DOM runtime into the bundle. +createVaporApp(App).mount('#app'); diff --git a/apps/vue-vapor/src/services/weatherService.js b/apps/vue-vapor/src/services/weatherService.js new file mode 100644 index 000000000..ab7c4dc79 --- /dev/null +++ b/apps/vue-vapor/src/services/weatherService.js @@ -0,0 +1,196 @@ +class WeatherService { + constructor() { + this.baseUrl = 'https://api.open-meteo.com/v1'; + this.geocodingUrl = 'https://geocoding-api.open-meteo.com/v1'; + this.useMockData = this.shouldUseMockData(); + } + + shouldUseMockData() { + // Check if we're in a testing environment (Playwright sets specific user agents) + const isTestEnvironment = navigator.userAgent.includes('Playwright') || + navigator.userAgent.includes('HeadlessChrome'); + + // Don't use mock data if we're explicitly testing API errors + if (window.location.search.includes('mock=false')) { + return false; + } + + // Use mock data if explicitly requested or if we're in a test environment + return window.location.search.includes('mock=true') || isTestEnvironment; + } + + async getMockData() { + try { + // Add delay in test environments to make loading state visible + if (this.isTestEnvironment()) { + await new Promise(resolve => setTimeout(resolve, 200)); + } + + const response = await fetch('/mocks/weather-data.json'); + if (!response.ok) { + throw new Error('Failed to load mock data'); + } + return await response.json(); + } catch (error) { + console.error('Error loading mock data:', error); + throw error; + } + } + + isTestEnvironment() { + return navigator.userAgent.includes('Playwright') || + navigator.userAgent.includes('HeadlessChrome'); + } + + getMockGeocodingData(cityName) { + // Mock geocoding data for different cities to enable proper testing + const mockCities = { + 'London': { + latitude: 51.5074, + longitude: -0.1278, + name: 'London', + country: 'United Kingdom' + }, + 'Tokyo': { + latitude: 35.6762, + longitude: 139.6503, + name: 'Tokyo', + country: 'Japan' + }, + 'Paris': { + latitude: 48.8566, + longitude: 2.3522, + name: 'Paris', + country: 'France' + }, + 'São Paulo': { + latitude: -23.5505, + longitude: -46.6333, + name: 'São Paulo', + country: 'Brazil' + }, + 'New York': { + latitude: 40.7128, + longitude: -74.0060, + name: 'New York', + country: 'United States' + }, + 'Zürich': { + latitude: 47.3769, + longitude: 8.5417, + name: 'Zürich', + country: 'Switzerland' + } + }; + + // Handle invalid cities + if (cityName.includes('Invalid') || cityName.includes('123') || !cityName.trim()) { + throw new Error('Unable to find location. Please check the city name and try again.'); + } + + // Return mock data for known cities, or default to London for unknown cities + return mockCities[cityName] || mockCities['London']; + } + + async geocodeLocation(cityName) { + if (this.useMockData) { + return this.getMockGeocodingData(cityName); + } + + try { + const response = await fetch( + `${this.geocodingUrl}/search?name=${encodeURIComponent(cityName)}&count=1&language=en&format=json` + ); + + if (!response.ok) { + throw new Error('Failed to geocode location'); + } + + const data = await response.json(); + + if (!data.results || data.results.length === 0) { + throw new Error(`Location "${cityName}" not found. Please check the spelling and try again.`); + } + + return data.results[0]; + } catch (error) { + console.error('Geocoding error:', error); + throw error; + } + } + + async getWeatherData(lat, lon) { + if (this.useMockData) { + return this.getMockData(); + } + + try { + const response = await fetch( + `${this.baseUrl}/forecast?` + + `latitude=${lat}&longitude=${lon}&` + + `current=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_direction_10m,pressure_msl,cloud_cover,precipitation,weather_code,is_day&` + + `daily=temperature_2m_max,temperature_2m_min,weather_code,sunrise,sunset,precipitation_probability_max,rain_sum,uv_index_max&` + + `timezone=auto` + ); + + if (!response.ok) { + throw new Error('Failed to fetch weather data'); + } + + return await response.json(); + } catch (error) { + console.error('Weather API error:', error); + throw error; + } + } + + async getWeatherByCity(cityName) { + try { + const location = await this.geocodeLocation(cityName); + const weather = await this.getWeatherData(location.latitude, location.longitude); + + return { + ...weather, + locationName: location.name, + country: location.country + }; + } catch (error) { + console.error('Weather service error:', error); + throw error; + } + } + + getCurrentLocationWeather() { + if (!navigator.geolocation) { + throw new Error('Geolocation not supported'); + } + + return new Promise((resolve, reject) => { + navigator.geolocation.getCurrentPosition( + async(position) => { + try { + const { latitude, longitude } = position.coords; + const weather = await this.getWeatherData(latitude, longitude); + resolve({ + ...weather, + locationName: 'Current Location' + }); + } catch (error) { + reject(error); + } + }, + (error) => { + console.error('Geolocation error:', error); + reject(new Error('Unable to get your location. Please search for a city instead.')); + }, + { + timeout: 10000, + enableHighAccuracy: false, + maximumAge: 300000 // 5 minutes + } + ); + }); + } +} + +export const weatherService = new WeatherService(); diff --git a/apps/vue-vapor/src/utils/weatherUtils.js b/apps/vue-vapor/src/utils/weatherUtils.js new file mode 100644 index 000000000..f41de9232 --- /dev/null +++ b/apps/vue-vapor/src/utils/weatherUtils.js @@ -0,0 +1,121 @@ +export class WeatherUtils { + static getWeatherDescription(weatherCode) { + const weatherCodes = { + 0: 'Clear sky', + 1: 'Mainly clear', + 2: 'Partly cloudy', + 3: 'Overcast', + 45: 'Fog', + 48: 'Depositing rime fog', + 51: 'Light drizzle', + 53: 'Moderate drizzle', + 55: 'Dense drizzle', + 56: 'Light freezing drizzle', + 57: 'Dense freezing drizzle', + 61: 'Slight rain', + 63: 'Moderate rain', + 65: 'Heavy rain', + 66: 'Light freezing rain', + 67: 'Heavy freezing rain', + 71: 'Slight snow fall', + 73: 'Moderate snow fall', + 75: 'Heavy snow fall', + 77: 'Snow grains', + 80: 'Slight rain showers', + 81: 'Moderate rain showers', + 82: 'Violent rain showers', + 85: 'Slight snow showers', + 86: 'Heavy snow showers', + 95: 'Thunderstorm', + 96: 'Thunderstorm with slight hail', + 99: 'Thunderstorm with heavy hail' + }; + + return weatherCodes[weatherCode] || 'Unknown'; + } + + static getWeatherIcon(weatherCode, isDay = 1) { + const iconMap = { + 0: isDay ? '☀️' : '🌙', + 1: isDay ? '🌤️' : '🌙', + 2: isDay ? '⛅' : '☁️', + 3: '☁️', + 45: '🌫️', + 48: '🌫️', + 51: '🌦️', + 53: '🌦️', + 55: '🌦️', + 56: '🌨️', + 57: '🌨️', + 61: '🌧️', + 63: '🌧️', + 65: '🌧️', + 66: '🌨️', + 67: '🌨️', + 71: '❄️', + 73: '❄️', + 75: '❄️', + 77: '❄️', + 80: '🌦️', + 81: '🌦️', + 82: '⛈️', + 85: '🌨️', + 86: '🌨️', + 95: '⛈️', + 96: '⛈️', + 99: '⛈️' + }; + + return iconMap[weatherCode] || '🌤️'; + } + + static getConditionClass(weatherCode) { + if ([0, 1].includes(weatherCode)) {return 'clear';} + if ([2, 3].includes(weatherCode)) {return 'cloudy';} + if ([45, 48].includes(weatherCode)) {return 'foggy';} + if ([51, 53, 55, 56, 57, 61, 63, 65, 66, 67, 80, 81, 82].includes(weatherCode)) {return 'rainy';} + if ([71, 73, 75, 77, 85, 86].includes(weatherCode)) {return 'snowy';} + if ([95, 96, 99].includes(weatherCode)) {return 'stormy';} + return 'clear'; + } + + static formatTemperature(temp) { + return `${Math.round(temp)}°C`; + } + + static formatWindSpeed(speed) { + return `${Math.round(speed)} km/h`; + } + + static formatPressure(pressure) { + return `${Math.round(pressure)} hPa`; + } + + static formatPercentage(value) { + return `${Math.round(value)}%`; + } + + static getWindDirection(degrees) { + const directions = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']; + const index = Math.round(degrees / 22.5) % 16; + return directions[index]; + } + + static formatDate(dateString) { + const date = new Date(dateString); + return date.toLocaleDateString('en-US', { + weekday: 'short', + month: 'short', + day: 'numeric' + }); + } + + static formatTime(timeString) { + const date = new Date(timeString); + return date.toLocaleTimeString('en-US', { + hour: '2-digit', + minute: '2-digit', + hour12: false + }); + } +} diff --git a/apps/vue-vapor/vite.config.js b/apps/vue-vapor/vite.config.js new file mode 100644 index 000000000..73ddd3296 --- /dev/null +++ b/apps/vue-vapor/vite.config.js @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; + +export default defineConfig({ + // Reason: keep every SFC on the Vapor compiler path, including future components + // that might accidentally omit the per-file `vapor` marker. + plugins: [vue({ features: { vapor: true, optionsAPI: false } })], + base: './', + server: { + port: 3000 + } +}); diff --git a/frameworks.json b/frameworks.json index 4b7960e99..cc291559a 100644 --- a/frameworks.json +++ b/frameworks.json @@ -262,6 +262,36 @@ "logo": "https://pixelflare.cc/alicia/logo/dashy/w256" } }, + { + "id": "vue-vapor", + "name": "Vue Vapor", + "displayName": "Vue Vapor Mode", + "dir": "vue-vapor", + "assetsDir": "public", + "build": { + "buildCommand": "vite build", + "hasNodeModules": true, + "devCommand": "vite --port 3000", + "testCommand": "playwright test --config=tests/config/playwright-vue-vapor.config.js", + "lintFiles": [ + "js", + "vue" + ] + }, + "meta": { + "emoji": "💨", + "iconName": "vuedotjs", + "website": "https://github.com/vuejs/core/releases/tag/v3.6.0-rc.2", + "docs": "https://github.com/vuejs/core/releases/tag/v3.6.0-rc.1#about-vapor-mode", + "github": "https://github.com/vuejs/core", + "npmPackage": "vue", + "color": "#42B883", + "accentColor": "#35495E", + "logo": "https://storage.googleapis.com/as93-screenshots/frontend-benchmarks/framework-logos/vue.png", + "description": "Opt-in Vue compiler mode using direct DOM updates without a virtual DOM", + "longDescription": "Compile-time Vue rendering mode that emits direct DOM operations and omits the Virtual DOM runtime." + } + }, { "id": "jquery", "name": "jQuery", diff --git a/package.json b/package.json index 070b9737f..4b81744df 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "start": "python scripts/run/serve.py", "help": "python scripts/main.py", "// DEV COMMANDS": "-------------------------------------------------------", - "dev:all": "npm run dev:alpine && npm run dev:angular && npm run dev:astro && npm run dev:geajs && npm run dev:jquery && npm run dev:lit && npm run dev:lume-js && npm run dev:preact && npm run dev:qwik && npm run dev:react && npm run dev:solid && npm run dev:svelte && npm run dev:vanilla && npm run dev:vanjs && npm run dev:vue", + "dev:all": "npm run dev:alpine && npm run dev:angular && npm run dev:astro && npm run dev:geajs && npm run dev:jquery && npm run dev:lit && npm run dev:lume-js && npm run dev:preact && npm run dev:qwik && npm run dev:react && npm run dev:solid && npm run dev:svelte && npm run dev:vanilla && npm run dev:vanjs && npm run dev:vue && npm run dev:vue-vapor", "dev:alpine": "cd apps/alpine && python3 -m http.server 3000 || python -m http.server 3000", "dev:angular": "cd apps/angular && npx ng serve --port 3000", "dev:astro": "cd apps/astro && npx astro dev --port 3000", @@ -33,8 +33,9 @@ "dev:vanilla": "cd apps/vanilla && python3 -m http.server 3000 || python -m http.server 3000", "dev:vanjs": "cd apps/vanjs && npx vite --port 3000", "dev:vue": "cd apps/vue && npx vite --port 3000", + "dev:vue-vapor": "cd apps/vue-vapor && npx vite --port 3000", "// BUILD COMMANDS": "-----------------------------------------------------", - "build:all": "npm run build:alpine && npm run build:angular && npm run build:astro && npm run build:geajs && npm run build:jquery && npm run build:lit && npm run build:lume-js && npm run build:preact && npm run build:qwik && npm run build:react && npm run build:solid && npm run build:svelte && npm run build:vanilla && npm run build:vanjs && npm run build:vue", + "build:all": "npm run build:alpine && npm run build:angular && npm run build:astro && npm run build:geajs && npm run build:jquery && npm run build:lit && npm run build:lume-js && npm run build:preact && npm run build:qwik && npm run build:react && npm run build:solid && npm run build:svelte && npm run build:vanilla && npm run build:vanjs && npm run build:vue && npm run build:vue-vapor", "build:alpine": "cd apps/alpine && echo 'No build step required'", "build:angular": "cd apps/angular && npx ng build", "build:astro": "cd apps/astro && npx astro build", @@ -50,8 +51,9 @@ "build:vanilla": "cd apps/vanilla && echo 'No build step required'", "build:vanjs": "cd apps/vanjs && npx vite build", "build:vue": "cd apps/vue && npx vite build", + "build:vue-vapor": "cd apps/vue-vapor && npx vite build", "// TEST COMMANDS": "------------------------------------------------------", - "test:all": "npm run test:alpine && npm run test:angular && npm run test:astro && npm run test:geajs && npm run test:jquery && npm run test:lit && npm run test:lume-js && npm run test:preact && npm run test:qwik && npm run test:react && npm run test:solid && npm run test:svelte && npm run test:vanilla && npm run test:vanjs && npm run test:vue", + "test:all": "npm run test:alpine && npm run test:angular && npm run test:astro && npm run test:geajs && npm run test:jquery && npm run test:lit && npm run test:lume-js && npm run test:preact && npm run test:qwik && npm run test:react && npm run test:solid && npm run test:svelte && npm run test:vanilla && npm run test:vanjs && npm run test:vue && npm run test:vue-vapor", "test:alpine": "npx playwright test --config=tests/config/playwright-alpine.config.js --reporter=list", "test:angular": "npx playwright test --config=tests/config/playwright-angular.config.js --reporter=list", "test:astro": "npx playwright test --config=tests/config/playwright-astro.config.js --reporter=list", @@ -67,8 +69,9 @@ "test:vanilla": "npx playwright test --config=tests/config/playwright-vanilla.config.js --reporter=list", "test:vanjs": "npx playwright test --config=tests/config/playwright-vanjs.config.js --reporter=list", "test:vue": "npx playwright test --config=tests/config/playwright-vue.config.js --reporter=list", + "test:vue-vapor": "npx playwright test --config=tests/config/playwright-vue-vapor.config.js --reporter=list", "// LINT COMMANDS": "------------------------------------------------------", - "lint:all": "npm run lint:alpine && npm run lint:angular && npm run lint:astro && npm run lint:geajs && npm run lint:jquery && npm run lint:lit && npm run lint:lume-js && npm run lint:preact && npm run lint:qwik && npm run lint:react && npm run lint:solid && npm run lint:svelte && npm run lint:vanilla && npm run lint:vanjs && npm run lint:vue", + "lint:all": "npm run lint:alpine && npm run lint:angular && npm run lint:astro && npm run lint:geajs && npm run lint:jquery && npm run lint:lit && npm run lint:lume-js && npm run lint:preact && npm run lint:qwik && npm run lint:react && npm run lint:solid && npm run lint:svelte && npm run lint:vanilla && npm run lint:vanjs && npm run lint:vue && npm run lint:vue-vapor", "lint:alpine": "eslint 'apps/alpine/**/*.js'", "lint:angular": "eslint 'apps/angular/**/*.{ts,html}'", "lint:astro": "eslint 'apps/astro/**/*.js'", @@ -84,6 +87,7 @@ "lint:vanilla": "eslint 'apps/vanilla/**/*.js'", "lint:vanjs": "eslint 'apps/vanjs/**/*.js'", "lint:vue": "eslint 'apps/vue/**/*.{js,vue}'", + "lint:vue-vapor": "eslint 'apps/vue-vapor/**/*.{js,vue}'", "// MISC SCRIPTS": "--------------------------------------------------------", "install:playwright": "playwright install", "generate-mocks": "python scripts/setup/generate_mocks.py", diff --git a/tests/config/playwright-vue-vapor.config.js b/tests/config/playwright-vue-vapor.config.js new file mode 100644 index 000000000..5673b1aae --- /dev/null +++ b/tests/config/playwright-vue-vapor.config.js @@ -0,0 +1,3 @@ +const { createConfig } = require('./playwright.config.base.js'); + +module.exports = createConfig('vue-vapor'); diff --git a/tests/config/playwright.config.base.js b/tests/config/playwright.config.base.js index 25d9f28e9..5576ccb3f 100644 --- a/tests/config/playwright.config.base.js +++ b/tests/config/playwright.config.base.js @@ -58,6 +58,13 @@ function createConfig(framework) { url: 'http://localhost:3000/', } }, + 'vue-vapor': { + baseURL: 'http://localhost:3000/?mock=true', + webServer: { + command: 'npm run dev:vue-vapor', + url: 'http://localhost:3000/', + } + }, jquery: { baseURL: 'http://localhost:3000/?mock=true', webServer: {