From 28d982e497bfd04351c8495ec9ddd58fc205e771 Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Sat, 14 Sep 2024 01:14:22 +0800 Subject: [PATCH] feat: add lighthouse endpoint --- pnpm-lock.yaml | 552 +++++++++++++++++- src/server/package.json | 1 + .../migration.sql | 21 + src/server/prisma/schema.prisma | 19 + src/server/prisma/zod/index.ts | 1 + .../prisma/zod/websitelighthousereport.ts | 13 + src/server/trpc/routers/website.ts | 130 +++++ src/server/utils/screenshot/lighthouse.ts | 35 ++ 8 files changed, 765 insertions(+), 7 deletions(-) create mode 100644 src/server/prisma/migrations/20240913094805_add_website_lh_report/migration.sql create mode 100644 src/server/prisma/zod/websitelighthousereport.ts create mode 100644 src/server/utils/screenshot/lighthouse.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7cc3f9c..2c7f140 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -508,6 +508,9 @@ importers: jsonwebtoken: specifier: ^9.0.2 version: 9.0.2 + lighthouse: + specifier: ^12.2.1 + version: 12.2.1 lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -2665,6 +2668,21 @@ packages: '@floating-ui/utils@0.2.1': resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} + '@formatjs/ecma402-abstract@2.0.0': + resolution: {integrity: sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==} + + '@formatjs/fast-memoize@2.2.0': + resolution: {integrity: sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==} + + '@formatjs/icu-messageformat-parser@2.7.8': + resolution: {integrity: sha512-nBZJYmhpcSX0WeJ5SDYUkZ42AgR3xiyhNCsQweFx3cz/ULJjym8bHAzWKvG5e2+1XO98dBYC0fWeeAECAVSwLA==} + + '@formatjs/icu-skeleton-parser@1.8.2': + resolution: {integrity: sha512-k4ERKgw7aKGWJZgTarIcNEmvyTVD9FYh0mTrrBMHZ1b8hUu6iOJ4SzsZlo3UNAvHYa+PnvntIwRPt1/vy4nA9Q==} + + '@formatjs/intl-localematcher@0.5.4': + resolution: {integrity: sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==} + '@gulpjs/to-absolute-glob@4.0.0': resolution: {integrity: sha512-kjotm7XJrJ6v+7knhPaRgaT6q8F8K2jiafwYdNHLzmV0uGLuZY43FK6smNSHUPrhq5kX2slCUy+RGG/xGqmIKA==} engines: {node: '>=10.13.0'} @@ -3009,6 +3027,9 @@ packages: '@paralleldrive/cuid2@2.2.2': resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} + '@paulirish/trace_engine@0.0.32': + resolution: {integrity: sha512-KxWFdRNbv13U8bhYaQvH6gLG9CVEt2jKeosyOOYILVntWEVWhovbgDrbOiZ12pJO3vjZs0Zgbd3/Zgde98woEA==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -3114,6 +3135,11 @@ packages: engines: {node: '>=16.3.0'} hasBin: true + '@puppeteer/browsers@2.4.0': + resolution: {integrity: sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==} + engines: {node: '>=18'} + hasBin: true + '@radix-ui/number@1.0.1': resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==} @@ -3899,6 +3925,30 @@ packages: cpu: [x64] os: [win32] + '@sentry/core@6.19.7': + resolution: {integrity: sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw==} + engines: {node: '>=6'} + + '@sentry/hub@6.19.7': + resolution: {integrity: sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA==} + engines: {node: '>=6'} + + '@sentry/minimal@6.19.7': + resolution: {integrity: sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ==} + engines: {node: '>=6'} + + '@sentry/node@6.19.7': + resolution: {integrity: sha512-gtmRC4dAXKODMpHXKfrkfvyBL3cI8y64vEi3fDD046uqYcrWdgoQsffuBbxMAizc6Ez1ia+f0Flue6p15Qaltg==} + engines: {node: '>=6'} + + '@sentry/types@6.19.7': + resolution: {integrity: sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==} + engines: {node: '>=6'} + + '@sentry/utils@6.19.7': + resolution: {integrity: sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA==} + engines: {node: '>=6'} + '@sideway/address@4.1.5': resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} @@ -4985,6 +5035,10 @@ packages: resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} engines: {node: '>= 10.0.0'} + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + agent-base@7.1.0: resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} engines: {node: '>= 14'} @@ -5052,6 +5106,10 @@ packages: ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -5262,12 +5320,19 @@ packages: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} + axe-core@4.10.0: + resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==} + engines: {node: '>=4'} + axios@1.5.0: resolution: {integrity: sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==} b4a@1.6.4: resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} + b4a@1.6.6: + resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + babel-loader@9.1.3: resolution: {integrity: sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==} engines: {node: '>= 14.15.0'} @@ -5307,6 +5372,18 @@ packages: bare-events@2.2.1: resolution: {integrity: sha512-9GYPpsPFvrWBkelIhOhTWtkeZxVxZOdb3VnFTCzlOo3OjvmTvzLoZFUT8kNFACx0vJej6QPney1Cf9BvzCNE/A==} + bare-fs@2.3.4: + resolution: {integrity: sha512-7YyxitZEq0ey5loOF5gdo1fZQFF7290GziT+VbAJ+JbYTJYaPZwuEz2r/Nq23sm4fjyTgUf2uJI2gkT3xAuSYA==} + + bare-os@2.4.3: + resolution: {integrity: sha512-FjkNiU3AwTQNQkcxFOmDcCfoN1LjjtU+ofGJh5DymZZLTqdw2i/CzV7G0h3snvh6G8jrWtdmNSgZPH4L2VOAsQ==} + + bare-path@2.1.3: + resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==} + + bare-stream@2.3.0: + resolution: {integrity: sha512-pVRWciewGUeCyKEuRxwv06M079r+fRjAQjBEK2P6OYGrO43O+Z0LrPZZEjlc4mB6C2RpZ9AxJ1s7NLEtOHO6eA==} + base-64@0.1.0: resolution: {integrity: sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==} @@ -5617,6 +5694,11 @@ packages: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} + chrome-launcher@1.1.2: + resolution: {integrity: sha512-YclTJey34KUm5jB1aEJCq807bSievi7Nb/TU4Gu504fUYi3jw3KCIaH6L7nFWQhdEgH3V+wCh+kKD1P5cXnfxw==} + engines: {node: '>=12.13.0'} + hasBin: true + chrome-trace-event@1.0.3: resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} engines: {node: '>=6.0'} @@ -5626,6 +5708,11 @@ packages: peerDependencies: devtools-protocol: '*' + chromium-bidi@0.6.5: + resolution: {integrity: sha512-RuLrmzYrxSb0s9SgpB+QN5jJucPduZQ/9SIe76MDxYJuecPW5mxMdacJ1f4EtgiV+R0p3sCkznTMvH0MPGFqjA==} + peerDependencies: + devtools-protocol: '*' + ci-info@3.8.0: resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} engines: {node: '>=8'} @@ -5883,6 +5970,10 @@ packages: config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + configstore@5.0.1: + resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} + engines: {node: '>=8'} + configstore@6.0.0: resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} engines: {node: '>=12'} @@ -6112,6 +6203,9 @@ packages: resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} engines: {node: '>=12'} + csp_evaluator@1.1.1: + resolution: {integrity: sha512-N3ASg0C4kNPUaNxt1XAvzHIVuzdtr8KLgfk1O8WDyimp1GisPAHESupArO2ieHk9QWbrJ/WkQODyh21Ps/xhxw==} + css-box-model@1.2.1: resolution: {integrity: sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==} @@ -6497,6 +6591,12 @@ packages: devtools-protocol@0.0.1203626: resolution: {integrity: sha512-nEzHZteIUZfGCZtTiS1fRpC8UZmsfD1SiyPvaUNvS13dvKf666OAm8YTi0+Ca3n1nLEyu49Cy4+dPWpaHFJk9g==} + devtools-protocol@0.0.1312386: + resolution: {integrity: sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==} + + devtools-protocol@0.0.1330662: + resolution: {integrity: sha512-pzh6YQ8zZfz3iKlCvgzVCu22NdpZ8hNmwU6WnQjNVquh0A9iVosPtNLWDwaWVGyrntQlltPFztTMK5Cg6lfCuw==} + dezalgo@1.0.4: resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} @@ -6704,6 +6804,10 @@ packages: enquire.js@2.1.6: resolution: {integrity: sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==} + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + ensure-type@1.5.1: resolution: {integrity: sha512-Dxe+mVF4MupV6eueWiFa6hUd9OL9lIM2/LqR40k1P+dwG+G2il2UigXTU9aQlaw+Y/N0BKSaTofNw73htTbC5g==} @@ -7633,6 +7737,10 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + http-link-header@1.1.3: + resolution: {integrity: sha512-3cZ0SRL8fb9MUlU3mKM61FcQvPfXx2dBrZW3Vbg5CXa8jFlK8OaEpePenLe1oEXQduhz8b0QjsqfS59QP4AJDQ==} + engines: {node: '>=6.0.0'} + http-parser-js@0.5.8: resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} @@ -7640,6 +7748,10 @@ packages: resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==} engines: {node: '>= 14'} + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + http-proxy-middleware@2.0.6: resolution: {integrity: sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==} engines: {node: '>=12.0.0'} @@ -7667,6 +7779,10 @@ packages: resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} engines: {node: '>=10.19.0'} + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + https-proxy-agent@6.2.1: resolution: {integrity: sha512-ONsE3+yfZF2caH5+bJlcddtWqNI3Gvs5A38+ngvljxaBiRXRswym2c7yf8UAeFpRFKjFNHIFEHqR/OLAWJzyiA==} engines: {node: '>= 14'} @@ -7679,6 +7795,10 @@ packages: resolution: {integrity: sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==} engines: {node: '>= 14'} + https-proxy-agent@7.0.5: + resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} + engines: {node: '>= 14'} + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -7747,6 +7867,9 @@ packages: engines: {node: '>=16.x'} hasBin: true + image-ssim@0.2.0: + resolution: {integrity: sha512-W7+sO6/yhxy83L0G7xR8YAc5Z5QFtYEXXRV6EaE8tuYBZJnA3gVgp3q7X7muhLZVodeb9UfvjSbwt9VJwjIYAg==} + immer@9.0.21: resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} @@ -7822,6 +7945,9 @@ packages: intersection-observer@0.12.2: resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} + intl-messageformat@10.5.14: + resolution: {integrity: sha512-IjC6sI0X7YRjjyVH9aUgdftcmZK7WXdHeil4KwbjDnRWjnVitKpAx3rr6t6di1joFp5188VqKcobOPA6mCLG/w==} + invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} @@ -8186,6 +8312,9 @@ packages: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} + jpeg-js@0.4.4: + resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==} + jquery-mousewheel@3.1.13: resolution: {integrity: sha512-GXhSjfOPyDemM005YCEHvzrEALhKDIswtxSHSR2e4K/suHVJKJxxRCGz3skPjNxjJjQa9AVSGGlYjv1M3VLIPg==} @@ -8195,6 +8324,10 @@ packages: js-cookie@2.2.1: resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} + js-library-detector@6.7.0: + resolution: {integrity: sha512-c80Qupofp43y4cJ7+8TTDN/AsDwLi5oOm/plBrWI+iQt485vKXCco+yVmOwEgdo9VOdsYTuV0UlTeetVPTriXA==} + engines: {node: '>=12'} + js-tiktoken@1.0.10: resolution: {integrity: sha512-ZoSxbGjvGyMT13x6ACo9ebhDha/0FHdKA+OsQcMOWcm1Zs7r90Rhk5lhERLzji+3rA7EKpXCgwXcM5fF3DMpdA==} @@ -8361,6 +8494,17 @@ packages: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} + lighthouse-logger@2.0.1: + resolution: {integrity: sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==} + + lighthouse-stack-packs@1.12.1: + resolution: {integrity: sha512-i4jTmg7tvZQFwNFiwB+nCK6a7ICR68Xcwo+VIVd6Spi71vBNFUlds5HiDrSbClZdkQDON2Bhqv+KKJIo5zkPeA==} + + lighthouse@12.2.1: + resolution: {integrity: sha512-3deFGaQ/eTCzQekgWUcgLdWAjS81KHPG406r+A61KKLsxEcORw34A1aixU6G2lgqpdd95QZpdoSKxyy/jJfsHg==} + engines: {node: '>=18.16'} + hasBin: true + lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -8519,6 +8663,9 @@ packages: resolution: {integrity: sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg==} engines: {node: '>=0.10.0'} + lookup-closest-locale@6.2.0: + resolution: {integrity: sha512-/c2kL+Vnp1jnV6K6RpDTHK3dgg0Tu2VVp+elEiJpjfS1UyY7AjOYHohRug6wT0OpoX2qFgNORndE9RqesfVxWQ==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -8559,6 +8706,9 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} + lru_map@0.3.3: + resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==} + lucide-react@0.358.0: resolution: {integrity: sha512-rBSptRjZTMBm24zsFhR6pK/NgbT18JegZGKcH4+1H3+UigMSRpeoWLtR/fAwMYwYnlJOZB+y8WpeHne9D6X6Kg==} peerDependencies: @@ -8618,6 +8768,9 @@ packages: engines: {node: '>= 18'} hasBin: true + marky@1.2.5: + resolution: {integrity: sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==} + material-colors@1.2.6: resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==} @@ -8758,6 +8911,9 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + metaviewport-parser@0.3.0: + resolution: {integrity: sha512-EoYJ8xfjQ6kpe9VbVHvZTZHiOl4HL1Z18CrZ+qahvLXT7ZO4YTC2JMyt5FaUp9JJp6J4Ybb/z7IsCXZt86/QkQ==} + methods@1.1.2: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} @@ -9638,6 +9794,9 @@ packages: parenthesis@3.1.8: resolution: {integrity: sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==} + parse-cache-control@1.0.1: + resolution: {integrity: sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==} + parse-entities@4.0.1: resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} @@ -10361,6 +10520,10 @@ packages: resolution: {integrity: sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==} engines: {node: '>= 14'} + proxy-agent@6.4.0: + resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==} + engines: {node: '>= 14'} + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -10391,6 +10554,10 @@ packages: resolution: {integrity: sha512-elPYPozrgiM3phSy7VDUJCVWQ07SPnOm78fpSaaSNFoQx5sur/MqhTSro9Wz8lOEjqCykGC6WRkwxDgmqcy1dQ==} engines: {node: '>=16.13.2'} + puppeteer-core@23.3.0: + resolution: {integrity: sha512-sB2SsVMFs4gKad5OCdv6w5vocvtEUrRl0zQqSyRPbo/cj1Ktbarmhxy02Zyb9R9HrssBcJDZbkrvBnbaesPyYg==} + engines: {node: '>=18'} + puppeteer@21.7.0: resolution: {integrity: sha512-Yy+UUy0b9siJezbhHO/heYUoZQUwyqDK1yOQgblTt0l97tspvDVFkcW9toBlnSvSfkDmMI3Dx9cZL6R8bDArHA==} engines: {node: '>=16.13.2'} @@ -11374,6 +11541,10 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + robots-parser@3.0.1: + resolution: {integrity: sha512-s+pyvQeIKIZ0dx5iJiQk1tPLJAWln39+MI5jtM8wnyws+G5azk+dMnMX0qfbqNetKKNgcWWOdi0sfm+FbQbgdQ==} + engines: {node: '>=10.0.0'} + robust-predicates@2.0.4: resolution: {integrity: sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg==} @@ -11531,6 +11702,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} @@ -11788,6 +11964,10 @@ packages: resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} engines: {node: '>=6.0.0'} + speedline-core@1.4.3: + resolution: {integrity: sha512-DI7/OuAUD+GMpR6dmu8lliO2Wg5zfeh+/xsdyJZCzd8o5JgFUjCeLsBDuZjIQJdwXS3J0L/uZYrELKYqx+PXog==} + engines: {node: '>=8.0'} + splaytree@3.1.2: resolution: {integrity: sha512-4OM2BJgC5UzrhVnnJA4BkHKGtjXNzzUfpQjCO8I05xYPsfS/VuQDwjCGGMi8rYQilHEV4j8NBqTFbls/PZEE7A==} @@ -11866,6 +12046,9 @@ packages: streamx@2.16.1: resolution: {integrity: sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==} + streamx@2.20.1: + resolution: {integrity: sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==} + strict-event-emitter@0.5.1: resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} @@ -12088,6 +12271,9 @@ packages: tar-fs@3.0.4: resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==} + tar-fs@3.0.6: + resolution: {integrity: sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==} + tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} @@ -12150,6 +12336,9 @@ packages: engines: {node: '>=10'} hasBin: true + text-decoder@1.2.0: + resolution: {integrity: sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==} + text-extensions@2.4.0: resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} engines: {node: '>=8'} @@ -12167,6 +12356,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + third-party-web@0.24.5: + resolution: {integrity: sha512-1rUOdMYpNTRajgk1F7CmHD26oA6rTKekBjHay854J6OkPXeNyPcR54rhWDaamlWyi9t2wAVPQESdedBhucmOLA==} + throttle-debounce@3.0.1: resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} engines: {node: '>=10'} @@ -12227,6 +12419,12 @@ packages: resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} engines: {node: '>=12'} + tldts-core@6.1.44: + resolution: {integrity: sha512-DMOTcn8pGmuY65zDPH+M1xAOVtBj7Phrah0HxuspKEu33hOArCDmk3R4UEVymtuN+HveeUlVgX0+RNf4mFkoAw==} + + tldts-icann@6.1.44: + resolution: {integrity: sha512-wdJCZvtuZofbYMZ/DeS1UCus/upPvacxsXjKlC0Cjmw67LeGrdr/AfswQpi2rAnby6tKxAl17Hf7rsQA00HuwQ==} + tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -12433,6 +12631,9 @@ packages: typed-array-length@1.0.4: resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + typed-query-selector@2.12.0: + resolution: {integrity: sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==} + typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} @@ -12628,6 +12829,9 @@ packages: url@0.11.3: resolution: {integrity: sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==} + urlpattern-polyfill@10.0.0: + resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} + urlpattern-polyfill@9.0.0: resolution: {integrity: sha512-WHN8KDQblxd32odxeIgo83rdVDE2bvdkb86it7bMhYZwWKJz0+O0RK/eZiHYnM+zgt/U7hAHOlCQGfjjvSkw2g==} @@ -13067,6 +13271,22 @@ packages: utf-8-validate: optional: true + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xdg-basedir@4.0.0: + resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} + engines: {node: '>=8'} + xdg-basedir@5.1.0: resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} engines: {node: '>=12'} @@ -13177,6 +13397,9 @@ packages: zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + zustand@4.4.1: resolution: {integrity: sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==} engines: {node: '>=12.7.0'} @@ -14188,7 +14411,7 @@ snapshots: '@babel/core': 7.24.0 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-plugin-utils': 7.24.0 - debug: 4.3.4 + debug: 4.3.6 lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -15986,6 +16209,30 @@ snapshots: '@floating-ui/utils@0.2.1': {} + '@formatjs/ecma402-abstract@2.0.0': + dependencies: + '@formatjs/intl-localematcher': 0.5.4 + tslib: 2.6.2 + + '@formatjs/fast-memoize@2.2.0': + dependencies: + tslib: 2.6.2 + + '@formatjs/icu-messageformat-parser@2.7.8': + dependencies: + '@formatjs/ecma402-abstract': 2.0.0 + '@formatjs/icu-skeleton-parser': 1.8.2 + tslib: 2.6.2 + + '@formatjs/icu-skeleton-parser@1.8.2': + dependencies: + '@formatjs/ecma402-abstract': 2.0.0 + tslib: 2.6.2 + + '@formatjs/intl-localematcher@0.5.4': + dependencies: + tslib: 2.6.2 + '@gulpjs/to-absolute-glob@4.0.0': dependencies: is-negated-glob: 1.0.0 @@ -16416,6 +16663,8 @@ snapshots: dependencies: '@noble/hashes': 1.3.2 + '@paulirish/trace_engine@0.0.32': {} + '@pkgjs/parseargs@0.11.0': optional: true @@ -16631,6 +16880,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@puppeteer/browsers@2.4.0': + dependencies: + debug: 4.3.6 + extract-zip: 2.0.1 + progress: 2.0.3 + proxy-agent: 6.4.0 + semver: 7.6.3 + tar-fs: 3.0.6 + unbzip2-stream: 1.4.3 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + '@radix-ui/number@1.0.1': dependencies: '@babel/runtime': 7.24.0 @@ -17440,6 +17702,46 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.9.5': optional: true + '@sentry/core@6.19.7': + dependencies: + '@sentry/hub': 6.19.7 + '@sentry/minimal': 6.19.7 + '@sentry/types': 6.19.7 + '@sentry/utils': 6.19.7 + tslib: 1.14.1 + + '@sentry/hub@6.19.7': + dependencies: + '@sentry/types': 6.19.7 + '@sentry/utils': 6.19.7 + tslib: 1.14.1 + + '@sentry/minimal@6.19.7': + dependencies: + '@sentry/hub': 6.19.7 + '@sentry/types': 6.19.7 + tslib: 1.14.1 + + '@sentry/node@6.19.7': + dependencies: + '@sentry/core': 6.19.7 + '@sentry/hub': 6.19.7 + '@sentry/types': 6.19.7 + '@sentry/utils': 6.19.7 + cookie: 0.4.2 + https-proxy-agent: 5.0.1 + lru_map: 0.3.3 + tslib: 1.14.1 + transitivePeerDependencies: + - supports-color + + '@sentry/types@6.19.7': {} + + '@sentry/utils@6.19.7': + dependencies: + '@sentry/types': 6.19.7 + tslib: 1.14.1 + '@sideway/address@4.1.5': dependencies: '@hapi/hoek': 9.3.0 @@ -19133,6 +19435,12 @@ snapshots: address@1.2.2: {} + agent-base@6.0.2: + dependencies: + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + agent-base@7.1.0: dependencies: debug: 4.3.4 @@ -19232,6 +19540,8 @@ snapshots: dependencies: string-width: 4.2.3 + ansi-colors@4.1.3: {} + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 @@ -19542,6 +19852,8 @@ snapshots: available-typed-arrays@1.0.5: {} + axe-core@4.10.0: {} + axios@1.5.0: dependencies: follow-redirects: 1.15.2 @@ -19552,6 +19864,9 @@ snapshots: b4a@1.6.4: {} + b4a@1.6.6: + optional: true + babel-loader@9.1.3(@babel/core@7.24.0)(webpack@5.90.3): dependencies: '@babel/core': 7.24.0 @@ -19599,6 +19914,27 @@ snapshots: bare-events@2.2.1: optional: true + bare-fs@2.3.4: + dependencies: + bare-events: 2.2.1 + bare-path: 2.1.3 + bare-stream: 2.3.0 + optional: true + + bare-os@2.4.3: + optional: true + + bare-path@2.1.3: + dependencies: + bare-os: 2.4.3 + optional: true + + bare-stream@2.3.0: + dependencies: + b4a: 1.6.6 + streamx: 2.20.1 + optional: true + base-64@0.1.0: {} base64-js@1.5.1: {} @@ -20004,6 +20340,15 @@ snapshots: chownr@2.0.0: {} + chrome-launcher@1.1.2: + dependencies: + '@types/node': 18.18.13 + escape-string-regexp: 4.0.0 + is-wsl: 2.2.0 + lighthouse-logger: 2.0.1 + transitivePeerDependencies: + - supports-color + chrome-trace-event@1.0.3: {} chromium-bidi@0.5.2(devtools-protocol@0.0.1203626): @@ -20012,6 +20357,13 @@ snapshots: mitt: 3.0.1 urlpattern-polyfill: 9.0.0 + chromium-bidi@0.6.5(devtools-protocol@0.0.1330662): + dependencies: + devtools-protocol: 0.0.1330662 + mitt: 3.0.1 + urlpattern-polyfill: 10.0.0 + zod: 3.23.8 + ci-info@3.8.0: {} citty@0.1.6: @@ -20277,6 +20629,15 @@ snapshots: ini: 1.3.8 proto-list: 1.2.4 + configstore@5.0.1: + dependencies: + dot-prop: 5.3.0 + graceful-fs: 4.2.11 + make-dir: 3.1.0 + unique-string: 2.0.0 + write-file-atomic: 3.0.3 + xdg-basedir: 4.0.0 + configstore@6.0.0: dependencies: dot-prop: 6.0.1 @@ -20499,6 +20860,8 @@ snapshots: dependencies: type-fest: 1.4.0 + csp_evaluator@1.1.1: {} + css-box-model@1.2.1: dependencies: tiny-invariant: 1.3.3 @@ -20925,6 +21288,10 @@ snapshots: devtools-protocol@0.0.1203626: {} + devtools-protocol@0.0.1312386: {} + + devtools-protocol@0.0.1330662: {} + dezalgo@1.0.4: dependencies: asap: 2.0.6 @@ -21242,6 +21609,11 @@ snapshots: enquire.js@2.1.6: {} + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + ensure-type@1.5.1: {} entities@1.1.2: {} @@ -21996,7 +22368,7 @@ snapshots: dependencies: basic-ftp: 5.0.3 data-uri-to-buffer: 6.0.1 - debug: 4.3.4 + debug: 4.3.6 fs-extra: 8.1.0 transitivePeerDependencies: - supports-color @@ -22587,6 +22959,8 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + http-link-header@1.1.3: {} + http-parser-js@0.5.8: {} http-proxy-agent@7.0.0: @@ -22596,6 +22970,13 @@ snapshots: transitivePeerDependencies: - supports-color + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.0 + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + http-proxy-middleware@2.0.6(@types/express@4.17.17): dependencies: '@types/http-proxy': 1.17.14 @@ -22630,6 +23011,13 @@ snapshots: quick-lru: 5.1.1 resolve-alpn: 1.2.1 + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + https-proxy-agent@6.2.1: dependencies: agent-base: 7.1.0 @@ -22640,7 +23028,7 @@ snapshots: https-proxy-agent@7.0.0: dependencies: agent-base: 7.1.0 - debug: 4.3.4 + debug: 4.3.6 transitivePeerDependencies: - supports-color @@ -22651,6 +23039,13 @@ snapshots: transitivePeerDependencies: - supports-color + https-proxy-agent@7.0.5: + dependencies: + agent-base: 7.1.0 + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + human-signals@2.1.0: {} human-signals@4.3.1: {} @@ -22726,6 +23121,8 @@ snapshots: dependencies: queue: 6.0.2 + image-ssim@0.2.0: {} + immer@9.0.21: {} import-fresh@3.3.0: @@ -22813,6 +23210,13 @@ snapshots: intersection-observer@0.12.2: {} + intl-messageformat@10.5.14: + dependencies: + '@formatjs/ecma402-abstract': 2.0.0 + '@formatjs/fast-memoize': 2.2.0 + '@formatjs/icu-messageformat-parser': 2.7.8 + tslib: 2.6.2 + invariant@2.2.4: dependencies: loose-envify: 1.4.0 @@ -23116,12 +23520,16 @@ snapshots: joycon@3.1.1: {} + jpeg-js@0.4.4: {} + jquery-mousewheel@3.1.13: {} jquery@3.7.1: {} js-cookie@2.2.1: {} + js-library-detector@6.7.0: {} + js-tiktoken@1.0.10: dependencies: base64-js: 1.5.1 @@ -23297,6 +23705,50 @@ snapshots: leven@3.1.0: {} + lighthouse-logger@2.0.1: + dependencies: + debug: 2.6.9 + marky: 1.2.5 + transitivePeerDependencies: + - supports-color + + lighthouse-stack-packs@1.12.1: {} + + lighthouse@12.2.1: + dependencies: + '@paulirish/trace_engine': 0.0.32 + '@sentry/node': 6.19.7 + axe-core: 4.10.0 + chrome-launcher: 1.1.2 + configstore: 5.0.1 + csp_evaluator: 1.1.1 + devtools-protocol: 0.0.1312386 + enquirer: 2.4.1 + http-link-header: 1.1.3 + intl-messageformat: 10.5.14 + jpeg-js: 0.4.4 + js-library-detector: 6.7.0 + lighthouse-logger: 2.0.1 + lighthouse-stack-packs: 1.12.1 + lodash: 4.17.21 + lookup-closest-locale: 6.2.0 + metaviewport-parser: 0.3.0 + open: 8.4.2 + parse-cache-control: 1.0.1 + puppeteer-core: 23.3.0 + robots-parser: 3.0.1 + semver: 5.7.2 + speedline-core: 1.4.3 + third-party-web: 0.24.5 + tldts-icann: 6.1.44 + ws: 7.5.9 + yargs: 17.7.2 + yargs-parser: 21.1.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + lilconfig@2.1.0: {} lilconfig@3.1.1: {} @@ -23425,6 +23877,8 @@ snapshots: longest@1.0.1: {} + lookup-closest-locale@6.2.0: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -23457,6 +23911,8 @@ snapshots: lru-cache@7.18.3: {} + lru_map@0.3.3: {} + lucide-react@0.358.0(react@18.2.0): dependencies: react: 18.2.0 @@ -23548,6 +24004,8 @@ snapshots: marked@11.2.0: {} + marky@1.2.5: {} + material-colors@1.2.6: {} maxmind@4.3.18: @@ -23877,6 +24335,8 @@ snapshots: merge2@1.4.1: {} + metaviewport-parser@0.3.0: {} + methods@1.1.2: {} micromark-core-commonmark@1.1.0: @@ -24310,7 +24770,7 @@ snapshots: micromark@3.2.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.4 + debug: 4.3.6 decode-named-character-reference: 1.0.2 micromark-core-commonmark: 1.1.0 micromark-factory-space: 1.1.0 @@ -24332,7 +24792,7 @@ snapshots: micromark@4.0.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.4 + debug: 4.3.6 decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.0 @@ -25067,6 +25527,8 @@ snapshots: parenthesis@3.1.8: {} + parse-cache-control@1.0.1: {} + parse-entities@4.0.1: dependencies: '@types/unist': 2.0.10 @@ -25952,6 +26414,19 @@ snapshots: transitivePeerDependencies: - supports-color + proxy-agent@6.4.0: + dependencies: + agent-base: 7.1.0 + debug: 4.3.6 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.5 + lru-cache: 7.18.3 + pac-proxy-agent: 7.0.1 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.2 + transitivePeerDependencies: + - supports-color + proxy-from-env@1.1.0: {} prr@1.0.1: @@ -25995,6 +26470,19 @@ snapshots: - supports-color - utf-8-validate + puppeteer-core@23.3.0: + dependencies: + '@puppeteer/browsers': 2.4.0 + chromium-bidi: 0.6.5(devtools-protocol@0.0.1330662) + debug: 4.3.6 + devtools-protocol: 0.0.1330662 + typed-query-selector: 2.12.0 + ws: 8.18.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + puppeteer@21.7.0(typescript@5.5.4): dependencies: '@puppeteer/browsers': 1.9.1 @@ -27548,6 +28036,8 @@ snapshots: dependencies: glob: 7.2.3 + robots-parser@3.0.1: {} + robust-predicates@2.0.4: {} robust-predicates@3.0.2: {} @@ -27741,6 +28231,8 @@ snapshots: dependencies: lru-cache: 6.0.0 + semver@7.6.3: {} + send@0.18.0: dependencies: debug: 2.6.9 @@ -28070,7 +28562,7 @@ snapshots: spdy-transport@3.0.0: dependencies: - debug: 4.3.4 + debug: 4.3.6 detect-node: 2.1.0 hpack.js: 2.1.6 obuf: 1.1.2 @@ -28089,6 +28581,12 @@ snapshots: transitivePeerDependencies: - supports-color + speedline-core@1.4.3: + dependencies: + '@types/node': 18.18.13 + image-ssim: 0.2.0 + jpeg-js: 0.4.4 + splaytree@3.1.2: {} split-string@3.1.0: @@ -28160,6 +28658,15 @@ snapshots: optionalDependencies: bare-events: 2.2.1 + streamx@2.20.1: + dependencies: + fast-fifo: 1.3.2 + queue-tick: 1.0.1 + text-decoder: 1.2.0 + optionalDependencies: + bare-events: 2.2.1 + optional: true + strict-event-emitter@0.5.1: {} string-convert@0.2.1: {} @@ -28306,7 +28813,7 @@ snapshots: dependencies: component-emitter: 1.3.1 cookiejar: 2.1.4 - debug: 4.3.4 + debug: 4.3.6 fast-safe-stringify: 2.1.1 form-data: 4.0.0 formidable: 2.1.2 @@ -28513,6 +29020,14 @@ snapshots: pump: 3.0.0 tar-stream: 3.1.6 + tar-fs@3.0.6: + dependencies: + pump: 3.0.0 + tar-stream: 3.1.6 + optionalDependencies: + bare-fs: 2.3.4 + bare-path: 2.1.3 + tar-stream@2.2.0: dependencies: bl: 4.1.0 @@ -28592,6 +29107,11 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 + text-decoder@1.2.0: + dependencies: + b4a: 1.6.6 + optional: true + text-extensions@2.4.0: {} text-hex@1.0.0: {} @@ -28606,6 +29126,8 @@ snapshots: dependencies: any-promise: 1.3.0 + third-party-web@0.24.5: {} + throttle-debounce@3.0.1: {} throttle-debounce@5.0.0: {} @@ -28652,6 +29174,12 @@ snapshots: titleize@3.0.0: {} + tldts-core@6.1.44: {} + + tldts-icann@6.1.44: + dependencies: + tldts-core: 6.1.44 + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 @@ -28879,6 +29407,8 @@ snapshots: for-each: 0.3.3 is-typed-array: 1.1.12 + typed-query-selector@2.12.0: {} + typedarray-to-buffer@3.1.5: dependencies: is-typedarray: 1.0.0 @@ -29124,6 +29654,8 @@ snapshots: punycode: 1.4.1 qs: 6.11.2 + urlpattern-polyfill@10.0.0: {} + urlpattern-polyfill@9.0.0: {} use-callback-ref@1.3.1(@types/react@18.2.78)(react@18.2.0): @@ -29728,6 +30260,10 @@ snapshots: ws@8.16.0: {} + ws@8.18.0: {} + + xdg-basedir@4.0.0: {} + xdg-basedir@5.1.0: {} xml-js@1.6.11: @@ -29836,6 +30372,8 @@ snapshots: zod@3.22.4: {} + zod@3.23.8: {} + zustand@4.4.1(@types/react@18.2.78)(immer@9.0.21)(react@18.2.0): dependencies: use-sync-external-store: 1.2.0(react@18.2.0) diff --git a/src/server/package.json b/src/server/package.json index 46a023f..65ea3fa 100644 --- a/src/server/package.json +++ b/src/server/package.json @@ -50,6 +50,7 @@ "is-localhost-ip": "^2.0.0", "isolated-vm": "^4.7.2", "jsonwebtoken": "^9.0.2", + "lighthouse": "^12.2.1", "lodash-es": "^4.17.21", "maxmind": "^4.3.18", "md5": "^2.3.0", diff --git a/src/server/prisma/migrations/20240913094805_add_website_lh_report/migration.sql b/src/server/prisma/migrations/20240913094805_add_website_lh_report/migration.sql new file mode 100644 index 0000000..c5d0e93 --- /dev/null +++ b/src/server/prisma/migrations/20240913094805_add_website_lh_report/migration.sql @@ -0,0 +1,21 @@ +-- CreateEnum +CREATE TYPE "WebsiteLighthouseReportStatus" AS ENUM ('Pending', 'Success', 'Failed'); + +-- CreateTable +CREATE TABLE "WebsiteLighthouseReport" ( + "id" VARCHAR(30) NOT NULL, + "websiteId" VARCHAR(30), + "createdAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMPTZ(6) NOT NULL, + "url" TEXT NOT NULL, + "result" TEXT NOT NULL, + "status" "WebsiteLighthouseReportStatus" NOT NULL DEFAULT 'Pending', + + CONSTRAINT "WebsiteLighthouseReport_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "WebsiteLighthouseReport_createdAt_idx" ON "WebsiteLighthouseReport"("createdAt"); + +-- CreateIndex +CREATE INDEX "WebsiteLighthouseReport_websiteId_idx" ON "WebsiteLighthouseReport"("websiteId"); diff --git a/src/server/prisma/schema.prisma b/src/server/prisma/schema.prisma index 9dd1b7f..4fefa35 100644 --- a/src/server/prisma/schema.prisma +++ b/src/server/prisma/schema.prisma @@ -249,6 +249,25 @@ model WebsiteSessionData { @@index([sessionId]) } +enum WebsiteLighthouseReportStatus { + Pending + Success + Failed +} + +model WebsiteLighthouseReport { + id String @id() @default(cuid()) @db.VarChar(30) + websiteId String? @db.VarChar(30) + createdAt DateTime @default(now()) @db.Timestamptz(6) + updatedAt DateTime @updatedAt @db.Timestamptz(6) + url String + result String // json string + status WebsiteLighthouseReportStatus @default(Pending) + + @@index([createdAt]) + @@index([websiteId]) +} + model Telemetry { id String @id @unique @default(cuid()) @db.VarChar(30) workspaceId String @db.VarChar(30) diff --git a/src/server/prisma/zod/index.ts b/src/server/prisma/zod/index.ts index 6b9f42f..cbda417 100644 --- a/src/server/prisma/zod/index.ts +++ b/src/server/prisma/zod/index.ts @@ -9,6 +9,7 @@ export * from "./websitesession.js" export * from "./websiteevent.js" export * from "./websiteeventdata.js" export * from "./websitesessiondata.js" +export * from "./websitelighthousereport.js" export * from "./telemetry.js" export * from "./telemetrysession.js" export * from "./telemetryevent.js" diff --git a/src/server/prisma/zod/websitelighthousereport.ts b/src/server/prisma/zod/websitelighthousereport.ts new file mode 100644 index 0000000..ed8c2de --- /dev/null +++ b/src/server/prisma/zod/websitelighthousereport.ts @@ -0,0 +1,13 @@ +import * as z from "zod" +import * as imports from "./schemas/index.js" +import { WebsiteLighthouseReportStatus } from "@prisma/client" + +export const WebsiteLighthouseReportModelSchema = z.object({ + id: z.string(), + websiteId: z.string().nullish(), + createdAt: z.date(), + updatedAt: z.date(), + url: z.string(), + result: z.string(), + status: z.nativeEnum(WebsiteLighthouseReportStatus), +}) diff --git a/src/server/trpc/routers/website.ts b/src/server/trpc/routers/website.ts index 7f4d44a..7ccd70e 100644 --- a/src/server/trpc/routers/website.ts +++ b/src/server/trpc/routers/website.ts @@ -1,5 +1,6 @@ import { OpenApiMetaInfo, + publicProcedure, router, workspaceOwnerProcedure, workspaceProcedure, @@ -32,6 +33,10 @@ import { } from '../../model/_schema/filter.js'; import dayjs from 'dayjs'; import { WebsiteQueryFilters } from '../../utils/prisma.js'; +import { WebsiteLighthouseReportStatus } from '@prisma/client'; +import { generateLighthouse } from '../../utils/screenshot/lighthouse.js'; +import { WebsiteLighthouseReportModelSchema } from '../../prisma/zod/websitelighthousereport.js'; +import { method } from 'lodash-es'; const websiteNameSchema = z.string().max(100); const websiteDomainSchema = z.union([ @@ -550,6 +555,131 @@ export const websiteRouter = router({ return websiteInfo; }), + generateLighthouseReport: workspaceProcedure + .meta( + buildWebsiteOpenapi({ + method: 'POST', + path: '/generateLighthouseReport', + }) + ) + .input( + z.object({ + websiteId: z.string().cuid2(), + url: z.string().url(), + }) + ) + .output(z.string()) + .mutation(async ({ input }) => { + const { websiteId, url } = input; + + const websiteInfo = await prisma.websiteLighthouseReport.create({ + data: { + url, + websiteId, + status: WebsiteLighthouseReportStatus.Pending, + result: '', + }, + }); + + generateLighthouse(url) + .then(async (result) => { + await prisma.websiteLighthouseReport.update({ + where: { + id: websiteInfo.id, + }, + data: { + status: WebsiteLighthouseReportStatus.Success, + result: JSON.stringify(result), + }, + }); + }) + .catch(async () => { + await prisma.websiteLighthouseReport.update({ + where: { + id: websiteInfo.id, + }, + data: { + status: WebsiteLighthouseReportStatus.Failed, + }, + }); + }); + + return 'success'; + }), + getLighthouseReport: workspaceProcedure + .meta( + buildWebsiteOpenapi({ + method: 'GET', + path: '/getLighthouseReport', + }) + ) + .input( + z.object({ + websiteId: z.string().cuid2(), + }) + ) + .output( + z.array( + WebsiteLighthouseReportModelSchema.pick({ + id: true, + status: true, + createdAt: true, + }) + ) + ) + .query(async ({ input }) => { + const { websiteId } = input; + + const list = await prisma.websiteLighthouseReport.findMany({ + where: { + websiteId, + }, + take: 10, + orderBy: { + createdAt: 'desc', + }, + select: { + id: true, + status: true, + createdAt: true, + }, + }); + + return list; + }), + getLighthouseJSON: publicProcedure + .meta({ + openapi: { + tags: [OPENAPI_TAG.WEBSITE], + protect: true, + method: 'GET', + path: '/lighthouse/{lighthouseId}', + }, + }) + .input( + z.object({ + lighthouseId: z.string().cuid2(), + }) + ) + .output(z.record(z.string(), z.any())) + .query(async ({ input }) => { + const { lighthouseId } = input; + + const res = await prisma.websiteLighthouseReport.findFirst({ + where: { + id: lighthouseId, + }, + select: { + result: true, + }, + }); + + try { + return JSON.parse(res?.result ?? '{}'); + } catch (err) { + return {}; + } + }), }); function buildWebsiteOpenapi(meta: OpenApiMetaInfo): OpenApiMeta { diff --git a/src/server/utils/screenshot/lighthouse.ts b/src/server/utils/screenshot/lighthouse.ts new file mode 100644 index 0000000..cec40a6 --- /dev/null +++ b/src/server/utils/screenshot/lighthouse.ts @@ -0,0 +1,35 @@ +import puppeteer from 'puppeteer'; +import lighthouse, { Result } from 'lighthouse'; + +export async function generateLighthouse(url: string): Promise { + // Use Puppeteer to launch headless Chrome + // - Omit `--enable-automation` (See https://github.com/GoogleChrome/lighthouse/issues/12988) + // - Don't use 800x600 default viewport + const browser = await puppeteer.launch({ + // Set to false if you want to see the script in action. + headless: 'new', + defaultViewport: null, + ignoreDefaultArgs: ['--enable-automation'], + }); + const page = await browser.newPage(); + + // Wait for Lighthouse to open url, then inject our stylesheet. + browser.on('targetchanged', async (target) => { + if (page && page.url() === url) { + await page.addStyleTag({ content: '* {color: red}' }); + } + }); + + // Lighthouse will open the URL. + // Puppeteer will observe `targetchanged` and inject our stylesheet. + const res = await lighthouse(url, undefined, undefined, page); + if (!res) { + throw new Error('Lighthouse failed to generate report'); + } + + const { lhr } = res; + + await browser.close(); + + return lhr; +}