From e2e36e57fc7af4cd07f33f9bb9407f46a6a77489 Mon Sep 17 00:00:00 2001 From: Niel Thiart Date: Sat, 31 May 2025 15:22:25 +0200 Subject: [PATCH] feat: enhance QR code generation and input handling - Updated QRCodeController to use a textarea for input instead of an input field for better user experience. - Implemented debouncing for input changes to optimize QR code generation. - Removed unnecessary SVG sanitization functions and adjusted SVG handling in QR code rendering. - Added default debounce delay option in QRCodeController. - Improved error handling in the application initialization process with a more structured error message display. - Removed inline css again. - Updated Vite configuration to include plugins for single file output and minification, along with CSP policies for security. --- .stylelintrc.json | 16 + index.html | 214 +------- package.json | 12 +- pnpm-lock.yaml | 687 ++++++++++++++++++++++++-- src/controllers/qr-code-controller.ts | 50 +- src/core/qr-generator.ts | 1 + src/main.ts | 20 +- src/style.css | 200 ++++++++ src/utils/svg-sanitizer.ts | 16 - vite.config.ts | 9 + 10 files changed, 931 insertions(+), 294 deletions(-) create mode 100644 .stylelintrc.json create mode 100644 src/style.css delete mode 100644 src/utils/svg-sanitizer.ts diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 0000000..e21e79a --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json.schemastore.org/stylelintrc.json", + "extends": ["stylelint-config-standard"], + "rules": { + "selector-class-pattern": null, + "color-hex-length": "short", + "declaration-empty-line-before": null, + "rule-empty-line-before": [ + "always-multi-line", + { + "except": ["first-nested"], + "ignore": ["after-comment"] + } + ] + } +} diff --git a/index.html b/index.html index 731ebbc..f72c0a9 100644 --- a/index.html +++ b/index.html @@ -33,215 +33,7 @@ } } - +
@@ -250,7 +42,7 @@
- +
- + diff --git a/package.json b/package.json index 969c573..8a0c242 100644 --- a/package.json +++ b/package.json @@ -12,24 +12,23 @@ "test:ui": "vitest --ui", "test:run": "vitest run", "test:coverage": "vitest run --coverage", - "lint": "pnpm run lint:ts && pnpm run lint:html && pnpm run lint:md", + "lint": "pnpm run lint:ts && pnpm run lint:css && pnpm run lint:html && pnpm run lint:md", "lint:ts": "eslint . --ext .ts,.tsx", "lint:html": "htmlhint --config=.htmlhintrc --ignore coverage/**,dist/**,node_modules/** \"**/*.html\"", + "lint:css": "stylelint \"src/**/*.css\"", "lint:md": "markdownlint --ignore coverage/**,dist/**,node_modules/** \"**/*.md\"", - "lint:fix": "pnpm run lint:ts --fix", + "lint:fix": "pnpm run lint:ts --fix && pnpm run lint:css --fix", "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,css,md,html}\"", "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,css,md,html}\"" }, "devDependencies": { "@eslint/js": "^9.27.0", - "@types/dompurify": "^3.2.0", "@types/eslint": "^9.6.1", "@types/qrcode-svg": "^1.1.5", "@typescript-eslint/eslint-plugin": "^8.33.0", "@typescript-eslint/parser": "^8.33.0", "@vitest/coverage-v8": "3.1.4", "@vitest/ui": "^3.1.4", - "dompurify": "^3.2.6", "eslint": "^9.27.0", "eslint-config-prettier": "^10.1.5", "eslint-plugin-prettier": "^5.4.0", @@ -38,9 +37,14 @@ "markdownlint-cli": "^0.45.0", "prettier": "^3.5.3", "qrcode-svg": "^1.1.0", + "stylelint": "^16.20.0", + "stylelint-config-prettier": "^9.0.5", + "stylelint-config-standard": "^38.0.0", "typescript": "~5.8.3", "vite": "^6.3.5", + "vite-plugin-minify": "^2.1.0", "vite-plugin-node-csp": "^0.1.3", + "vite-plugin-singlefile": "^2.2.0", "vitest": "^3.1.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 253c154..ce35244 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,9 +11,6 @@ importers: '@eslint/js': specifier: ^9.27.0 version: 9.27.0 - '@types/dompurify': - specifier: ^3.2.0 - version: 3.2.0 '@types/eslint': specifier: ^9.6.1 version: 9.6.1 @@ -32,9 +29,6 @@ importers: '@vitest/ui': specifier: ^3.1.4 version: 3.1.4(vitest@3.1.4) - dompurify: - specifier: ^3.2.6 - version: 3.2.6 eslint: specifier: ^9.27.0 version: 9.27.0 @@ -59,15 +53,30 @@ importers: qrcode-svg: specifier: ^1.1.0 version: 1.1.0 + stylelint: + specifier: ^16.20.0 + version: 16.20.0(typescript@5.8.3) + stylelint-config-prettier: + specifier: ^9.0.5 + version: 9.0.5(stylelint@16.20.0(typescript@5.8.3)) + stylelint-config-standard: + specifier: ^38.0.0 + version: 38.0.0(stylelint@16.20.0(typescript@5.8.3)) typescript: specifier: ~5.8.3 version: 5.8.3 vite: specifier: ^6.3.5 version: 6.3.5(@types/node@22.15.23)(sass@1.89.0)(terser@5.40.0) + vite-plugin-minify: + specifier: ^2.1.0 + version: 2.1.0(vite@6.3.5(@types/node@22.15.23)(sass@1.89.0)(terser@5.40.0)) vite-plugin-node-csp: specifier: ^0.1.3 version: 0.1.3(vite@6.3.5(@types/node@22.15.23)(sass@1.89.0)(terser@5.40.0)) + vite-plugin-singlefile: + specifier: ^2.2.0 + version: 2.2.0(rollup@4.41.1)(vite@6.3.5(@types/node@22.15.23)(sass@1.89.0)(terser@5.40.0)) vitest: specifier: ^3.1.4 version: 3.1.4(@types/debug@4.1.12)(@types/node@22.15.23)(@vitest/ui@3.1.4)(jsdom@26.1.0)(sass@1.89.0)(terser@5.40.0) @@ -81,6 +90,10 @@ packages: '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -130,6 +143,22 @@ packages: resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} + '@csstools/media-query-list-parser@4.0.3': + resolution: {integrity: sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/selector-specificity@5.0.0': + resolution: {integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==} + engines: {node: '>=18'} + peerDependencies: + postcss-selector-parser: ^7.0.0 + + '@dual-bundle/import-meta-resolve@4.1.0': + resolution: {integrity: sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==} + '@esbuild/aix-ppc64@0.25.5': resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} engines: {node: '>=18'} @@ -374,6 +403,9 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@keyv/serialize@1.0.3': + resolution: {integrity: sha512-qnEovoOp5Np2JDGonIDL6Ayihw0RhnRh6vxPuHo4RDn1UOzwEo4AeIfpL6UGIrsceWrCMiVPgwRjbHu4vYFc3g==} + '@miniflare/core@2.14.4': resolution: {integrity: sha512-FMmZcC1f54YpF4pDWPtdQPIO8NXfgUxCoR9uyrhxKJdZu7M6n8QKopPVNuaxR40jcsdxb7yKoQoFWnHfzJD9GQ==} engines: {node: '>=16.13'} @@ -610,16 +642,15 @@ packages: '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - '@types/dompurify@3.2.0': - resolution: {integrity: sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==} - deprecated: This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed. - '@types/eslint@9.6.1': resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/html-minifier-terser@7.0.2': + resolution: {integrity: sha512-mm2HqV22l8lFQh4r2oSsOEVea+m0qqxEmwpc9kC1p/XzmjLWrReR9D/GRs8Pex2NX/imyEH9c5IU/7tMBQCHOA==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -635,9 +666,6 @@ packages: '@types/qrcode-svg@1.1.5': resolution: {integrity: sha512-GjkD+HB8S1wrIsf3skHDtcYBjzNhTxocMbX+wG166xDkaVOnLiMUla7bLjbwxo6mMvqqWQNP0Dk8nkIeizSmnw==} - '@types/trusted-types@2.0.7': - resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -758,6 +786,9 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -777,16 +808,30 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@2.0.0: + resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -800,6 +845,9 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + builtins@5.1.0: resolution: {integrity: sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==} @@ -811,10 +859,16 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + cacheable@1.9.0: + resolution: {integrity: sha512-8D5htMCxPDUULux9gFzv30f04Xo3wCnik0oOxKoRTPIBoqA7HtOcJ87uBhQTs3jCfZZTrUBGsYIZOgE0ZRgMAg==} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} @@ -840,6 +894,10 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + clean-css@5.3.3: + resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} + engines: {node: '>= 10.0'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -847,6 +905,13 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} @@ -865,10 +930,32 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + css-functions-list@3.2.3: + resolution: {integrity: sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==} + engines: {node: '>=12 || >=16'} + + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + cssstyle@4.3.1: resolution: {integrity: sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q==} engines: {node: '>=18'} @@ -915,8 +1002,12 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - dompurify@3.2.6: - resolution: {integrity: sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==} + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} dotenv@10.0.0: resolution: {integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==} @@ -939,6 +1030,13 @@ packages: resolution: {integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==} engines: {node: '>=0.12'} + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} @@ -1040,6 +1138,13 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.0.6: + resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -1054,6 +1159,9 @@ packages: fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + file-entry-cache@10.1.0: + resolution: {integrity: sha512-Et/ex6smi3wOOB+n5mek+Grf7P2AxZR5ueqRUvAAn4qkyatXi3cUC1cuQXVkX0VlzBVsN4BkWJFmY/fYiRTdww==} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -1070,6 +1178,9 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} + flat-cache@6.1.9: + resolution: {integrity: sha512-DUqiKkTlAfhtl7g78IuwqYM+YqvT+as0mY+EVk6mfimy19U79pJCzDZQsnqk3Ou/T6hFXWLGbwbADzD/c8Tydg==} + flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} @@ -1111,10 +1222,25 @@ packages: engines: {node: '>=12'} deprecated: Glob versions prior to v9 are no longer supported + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globjoin@0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} @@ -1122,6 +1248,9 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + hookified@1.9.0: + resolution: {integrity: sha512-2yEEGqphImtKIe1NXWEhu6yD3hlFR4Mxk4Mtp3XEyScpSt4pQ4ymmXA1zzxZpj99QkFK+nN0nzjeb2+RUi/6CQ==} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -1129,9 +1258,18 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-minifier-terser@7.2.0: + resolution: {integrity: sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + html-rewriter-wasm@0.4.1: resolution: {integrity: sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q==} + html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} + htmlhint@1.2.0: resolution: {integrity: sha512-ZUrnX99JOj3IG8W/Dn5wWcZ5mfbJRGeF3whxGKMtUHYHHSrAqdng7KlgHcPr9D5g4WAhTfCCpClsv8WEtxXgrg==} engines: {node: '>=20'} @@ -1153,6 +1291,9 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1179,6 +1320,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + ini@4.1.3: resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -1189,6 +1333,9 @@ packages: is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} @@ -1211,6 +1358,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -1244,6 +1395,9 @@ packages: resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} engines: {node: 20 || >=22} + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -1260,9 +1414,15 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -1280,14 +1440,27 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + keyv@5.3.3: + resolution: {integrity: sha512-Rwu4+nXI9fqcxiEHtbkvoes2X+QfkTRo1TMkPfwzipGsJlJO/z69vqB4FNl9xJ3xCpAcbkvmEabZfPzrwN3+gQ==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + known-css-properties@0.36.0: + resolution: {integrity: sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA==} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} @@ -1298,9 +1471,15 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + loupe@3.1.3: resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -1331,9 +1510,19 @@ packages: resolution: {integrity: sha512-xaSxkaU7wY/0852zGApM8LdlIfGCW8ETZ0Rr62IQtAnUMlMuifsg09vWJcNYeL4f0anvr8Vo4ZQar8jGpV0btQ==} engines: {node: '>=20'} + mathml-tag-names@2.1.3: + resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -1461,6 +1650,9 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} @@ -1473,6 +1665,10 @@ packages: encoding: optional: true + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + npm-run-path@5.3.0: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1505,6 +1701,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -1512,12 +1711,19 @@ packages: parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + parse-package-name@1.0.0: resolution: {integrity: sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==} parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1538,6 +1744,10 @@ packages: resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} engines: {node: 20 || >=22} + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -1556,6 +1766,22 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + postcss-resolve-nested-selector@0.1.6: + resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} + + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.5.3: resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} @@ -1592,10 +1818,22 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + relateurl@0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -1657,6 +1895,14 @@ packages: resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} engines: {node: '>=18'} + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + smol-toml@1.3.4: resolution: {integrity: sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==} engines: {node: '>= 18'} @@ -1706,10 +1952,41 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + stylelint-config-prettier@9.0.5: + resolution: {integrity: sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA==} + engines: {node: '>= 12'} + hasBin: true + peerDependencies: + stylelint: '>= 11.x < 15' + + stylelint-config-recommended@16.0.0: + resolution: {integrity: sha512-4RSmPjQegF34wNcK1e1O3Uz91HN8P1aFdFzio90wNK9mjgAI19u5vsU868cVZboKzCaa5XbpvtTzAAGQAxpcXA==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.16.0 + + stylelint-config-standard@38.0.0: + resolution: {integrity: sha512-uj3JIX+dpFseqd/DJx8Gy3PcRAJhlEZ2IrlFOc4LUxBX/PNMEQ198x7LCOE2Q5oT9Vw8nyc4CIL78xSqPr6iag==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.18.0 + + stylelint@16.20.0: + resolution: {integrity: sha512-B5Myu9WRxrgKuLs3YyUXLP2H0mrbejwNxPmyADlACWwFsrL8Bmor/nTSh4OMae5sHjOz6gkSeccQH34gM4/nAw==} + engines: {node: '>=18.12.0'} + hasBin: true + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-hyperlinks@3.2.0: + resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} + engines: {node: '>=14.18'} + + svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -1717,6 +1994,10 @@ packages: resolution: {integrity: sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==} engines: {node: ^14.18.0 || >=16.0.0} + table@6.9.0: + resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} + engines: {node: '>=10.0.0'} + terser@5.40.0: resolution: {integrity: sha512-cfeKl/jjwSR5ar7d0FGmave9hFGJT8obyo0z+CrQOylLDbk7X81nPU6vq9VORa5jU30SkDnT2FXjLbR8HLP+xA==} engines: {node: '>=10'} @@ -1780,6 +2061,9 @@ packages: peerDependencies: typescript: '>=4.8.4' + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -1805,6 +2089,9 @@ packages: urlpattern-polyfill@4.0.3: resolution: {integrity: sha512-DOE84vZT2fEcl9gqCUTcnAw5ZY5Id55ikUcziSUntuEFL3pRvavg5kwDmTEUJkeCHInTlV/HexFomgYnzO5kdQ==} + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + validate-npm-package-name@4.0.0: resolution: {integrity: sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -1814,12 +2101,24 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true + vite-plugin-minify@2.1.0: + resolution: {integrity: sha512-h+Ae0WX0mvrWM4GhE6JX2NmxlDuZRv4AgcTHFqfbSyPwS+OdlOM692AQrK0eO8lDEh7gYLjG3EHovKvtrNQDvA==} + peerDependencies: + vite: '>=5' + vite-plugin-node-csp@0.1.3: resolution: {integrity: sha512-+RH/bKzH53rWoYCtHgYbTqNwOIHCZbZ2Hiw0DSwfkeBYBzTUXlgFw1EROxL1hWJDX2Xa8b0vAJNkZIJF1iOm+A==} engines: {node: '>= 20'} peerDependencies: vite: ^4.0.0 || ^5.0.0 || ^6.0.0 + vite-plugin-singlefile@2.2.0: + resolution: {integrity: sha512-Ik1wXmJaGzeQtUeIV7JprDUqqy6DlLzXAY27Blei5peE4c9VJF+Kp9xWDJeuX0RJUZmFbIAuw1/RAh06A+Ql7w==} + engines: {node: '>18.0.0'} + peerDependencies: + rollup: ^4.35.0 + vite: ^5.4.11 || ^6.0.0 + vite@6.3.5: resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -1914,6 +2213,10 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1939,6 +2242,10 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ws@8.18.2: resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} engines: {node: '>=10.0.0'} @@ -1980,6 +2287,12 @@ snapshots: '@csstools/css-tokenizer': 3.0.4 lru-cache: 10.4.3 + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.27.1': {} @@ -2015,6 +2328,17 @@ snapshots: '@csstools/css-tokenizer@3.0.4': {} + '@csstools/media-query-list-parser@4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.1.0)': + dependencies: + postcss-selector-parser: 7.1.0 + + '@dual-bundle/import-meta-resolve@4.1.0': {} + '@esbuild/aix-ppc64@0.25.5': optional: true @@ -2176,7 +2500,6 @@ snapshots: dependencies: '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 - optional: true '@jridgewell/sourcemap-codec@1.5.0': {} @@ -2185,6 +2508,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@keyv/serialize@1.0.3': + dependencies: + buffer: 6.0.3 + '@miniflare/core@2.14.4': dependencies: '@iarna/toml': 2.2.5 @@ -2368,10 +2695,6 @@ snapshots: dependencies: '@types/ms': 2.1.0 - '@types/dompurify@3.2.0': - dependencies: - dompurify: 3.2.6 - '@types/eslint@9.6.1': dependencies: '@types/estree': 1.0.7 @@ -2379,6 +2702,8 @@ snapshots: '@types/estree@1.0.7': {} + '@types/html-minifier-terser@7.0.2': {} + '@types/json-schema@7.0.15': {} '@types/katex@0.16.7': {} @@ -2391,9 +2716,6 @@ snapshots: '@types/qrcode-svg@1.1.5': {} - '@types/trusted-types@2.0.7': - optional: true - '@types/unist@2.0.11': {} '@typescript-eslint/eslint-plugin@8.33.0(@typescript-eslint/parser@8.33.0(eslint@9.27.0)(typescript@5.8.3))(eslint@9.27.0)(typescript@5.8.3)': @@ -2572,6 +2894,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.6 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-regex@5.0.1: {} ansi-regex@6.1.0: {} @@ -2584,12 +2913,20 @@ snapshots: argparse@2.0.1: {} + array-union@2.1.0: {} + assertion-error@2.0.1: {} + astral-regex@2.0.0: {} + async@3.2.6: {} balanced-match@1.0.2: {} + balanced-match@2.0.0: {} + + base64-js@1.5.1: {} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -2603,8 +2940,12 @@ snapshots: dependencies: fill-range: 7.1.1 - buffer-from@1.1.2: - optional: true + buffer-from@1.1.2: {} + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 builtins@5.1.0: dependencies: @@ -2616,8 +2957,18 @@ snapshots: cac@6.7.14: {} + cacheable@1.9.0: + dependencies: + hookified: 1.9.0 + keyv: 5.3.3 + callsites@3.1.0: {} + camel-case@4.1.2: + dependencies: + pascal-case: 3.1.2 + tslib: 2.8.1 + chai@5.2.0: dependencies: assertion-error: 2.0.1 @@ -2644,29 +2995,54 @@ snapshots: readdirp: 4.1.2 optional: true + clean-css@5.3.3: + dependencies: + source-map: 0.6.1 + color-convert@2.0.1: dependencies: color-name: 1.1.4 color-name@1.1.4: {} + colord@2.9.3: {} + + commander@10.0.1: {} + commander@11.1.0: {} commander@13.1.0: {} - commander@2.20.3: - optional: true + commander@2.20.3: {} commander@8.3.0: {} concat-map@0.0.1: {} + cosmiconfig@9.0.0(typescript@5.8.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.8.3 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + css-functions-list@3.2.3: {} + + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + + cssesc@3.0.0: {} + cssstyle@4.3.1: dependencies: '@asamuzakjp/css-color': 3.2.0 @@ -2702,9 +3078,14 @@ snapshots: dependencies: dequal: 2.0.3 - dompurify@3.2.6: - optionalDependencies: - '@types/trusted-types': 2.0.7 + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 dotenv@10.0.0: {} @@ -2718,6 +3099,12 @@ snapshots: entities@6.0.0: {} + env-paths@2.2.1: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + es-module-lexer@1.7.0: {} esbuild@0.25.5: @@ -2865,6 +3252,10 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.0.6: {} + + fastest-levenshtein@1.0.16: {} + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -2875,6 +3266,10 @@ snapshots: fflate@0.8.2: {} + file-entry-cache@10.1.0: + dependencies: + flat-cache: 6.1.9 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -2893,6 +3288,12 @@ snapshots: flatted: 3.3.3 keyv: 4.5.4 + flat-cache@6.1.9: + dependencies: + cacheable: 1.9.0 + flatted: 3.3.3 + hookified: 1.9.0 + flatted@3.3.3: {} foreground-child@3.3.1: @@ -2941,20 +3342,55 @@ snapshots: minimatch: 5.1.6 once: 1.4.0 + global-modules@2.0.0: + dependencies: + global-prefix: 3.0.0 + + global-prefix@3.0.0: + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + globals@14.0.0: {} + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globjoin@0.1.4: {} + graphemer@1.4.0: {} has-flag@4.0.0: {} + hookified@1.9.0: {} + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 html-escaper@2.0.2: {} + html-minifier-terser@7.2.0: + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 10.0.1 + entities: 4.5.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.40.0 + html-rewriter-wasm@0.4.1: {} + html-tags@3.3.1: {} + htmlhint@1.2.0: dependencies: async: 3.2.6 @@ -2988,6 +3424,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ieee754@1.2.1: {} + ignore@5.3.2: {} ignore@7.0.4: {} @@ -3009,6 +3447,8 @@ snapshots: inherits@2.0.4: {} + ini@1.3.8: {} + ini@4.1.3: {} is-alphabetical@2.0.1: {} @@ -3018,6 +3458,8 @@ snapshots: is-alphabetical: 2.0.1 is-decimal: 2.0.1 + is-arrayish@0.2.1: {} + is-decimal@2.0.1: {} is-extglob@2.1.1: {} @@ -3032,6 +3474,8 @@ snapshots: is-number@7.0.0: {} + is-plain-object@5.0.0: {} + is-potential-custom-element-name@1.0.1: {} is-stream@3.0.0: {} @@ -3069,6 +3513,8 @@ snapshots: dependencies: '@isaacs/cliui': 8.0.2 + js-tokens@4.0.0: {} + js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -3102,8 +3548,12 @@ snapshots: json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} jsonc-parser@3.3.1: {} @@ -3118,13 +3568,23 @@ snapshots: dependencies: json-buffer: 3.0.1 + keyv@5.3.3: + dependencies: + '@keyv/serialize': 1.0.3 + + kind-of@6.0.3: {} + kleur@4.1.5: {} + known-css-properties@0.36.0: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 + lines-and-columns@1.2.4: {} + linkify-it@5.0.0: dependencies: uc.micro: 2.1.0 @@ -3135,8 +3595,14 @@ snapshots: lodash.merge@4.6.2: {} + lodash.truncate@4.4.2: {} + loupe@3.1.3: {} + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + lru-cache@10.4.3: {} lru-cache@11.1.0: {} @@ -3193,8 +3659,14 @@ snapshots: transitivePeerDependencies: - supports-color + mathml-tag-names@2.1.3: {} + + mdn-data@2.12.2: {} + mdurl@2.0.0: {} + meow@13.2.0: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -3406,6 +3878,11 @@ snapshots: natural-compare@1.4.0: {} + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + node-addon-api@7.1.1: optional: true @@ -3413,6 +3890,8 @@ snapshots: dependencies: whatwg-url: 5.0.0 + normalize-path@3.0.0: {} + npm-run-path@5.3.0: dependencies: path-key: 4.0.0 @@ -3453,6 +3932,11 @@ snapshots: package-json-from-dist@1.0.1: {} + param-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -3467,12 +3951,24 @@ snapshots: is-decimal: 2.0.1 is-hexadecimal: 2.0.1 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + parse-package-name@1.0.0: {} parse5@7.3.0: dependencies: entities: 6.0.0 + pascal-case@3.1.2: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + path-exists@4.0.0: {} path-key@3.1.1: {} @@ -3489,6 +3985,8 @@ snapshots: lru-cache: 11.1.0 minipass: 7.1.2 + path-type@4.0.0: {} + pathe@2.0.3: {} pathval@2.0.0: {} @@ -3499,6 +3997,19 @@ snapshots: picomatch@4.0.2: {} + postcss-resolve-nested-selector@0.1.6: {} + + postcss-safe-parser@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + + postcss-selector-parser@7.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + postcss@8.5.3: dependencies: nanoid: 3.3.11 @@ -3524,8 +4035,14 @@ snapshots: readdirp@4.1.2: optional: true + relateurl@0.2.7: {} + + require-from-string@2.0.2: {} + resolve-from@4.0.0: {} + resolve-from@5.0.0: {} + reusify@1.1.0: {} rollup@4.41.1: @@ -3604,6 +4121,14 @@ snapshots: mrmime: 2.0.1 totalist: 3.0.1 + slash@3.0.0: {} + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + smol-toml@1.3.4: {} source-map-js@1.2.1: {} @@ -3612,10 +4137,8 @@ snapshots: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - optional: true - source-map@0.6.1: - optional: true + source-map@0.6.1: {} stackback@0.0.2: {} @@ -3647,23 +4170,94 @@ snapshots: strip-json-comments@3.1.1: {} + stylelint-config-prettier@9.0.5(stylelint@16.20.0(typescript@5.8.3)): + dependencies: + stylelint: 16.20.0(typescript@5.8.3) + + stylelint-config-recommended@16.0.0(stylelint@16.20.0(typescript@5.8.3)): + dependencies: + stylelint: 16.20.0(typescript@5.8.3) + + stylelint-config-standard@38.0.0(stylelint@16.20.0(typescript@5.8.3)): + dependencies: + stylelint: 16.20.0(typescript@5.8.3) + stylelint-config-recommended: 16.0.0(stylelint@16.20.0(typescript@5.8.3)) + + stylelint@16.20.0(typescript@5.8.3): + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/media-query-list-parser': 4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0) + '@dual-bundle/import-meta-resolve': 4.1.0 + balanced-match: 2.0.0 + colord: 2.9.3 + cosmiconfig: 9.0.0(typescript@5.8.3) + css-functions-list: 3.2.3 + css-tree: 3.1.0 + debug: 4.4.1 + fast-glob: 3.3.3 + fastest-levenshtein: 1.0.16 + file-entry-cache: 10.1.0 + global-modules: 2.0.0 + globby: 11.1.0 + globjoin: 0.1.4 + html-tags: 3.3.1 + ignore: 7.0.4 + imurmurhash: 0.1.4 + is-plain-object: 5.0.0 + known-css-properties: 0.36.0 + mathml-tag-names: 2.1.3 + meow: 13.2.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.3 + postcss-resolve-nested-selector: 0.1.6 + postcss-safe-parser: 7.0.1(postcss@8.5.3) + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 + resolve-from: 5.0.0 + string-width: 4.2.3 + supports-hyperlinks: 3.2.0 + svg-tags: 1.0.0 + table: 6.9.0 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + - typescript + supports-color@7.2.0: dependencies: has-flag: 4.0.0 + supports-hyperlinks@3.2.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + + svg-tags@1.0.0: {} + symbol-tree@3.2.4: {} synckit@0.11.6: dependencies: '@pkgr/core': 0.2.4 + table@6.9.0: + dependencies: + ajv: 8.17.1 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + terser@5.40.0: dependencies: '@jridgewell/source-map': 0.3.6 acorn: 8.14.1 commander: 2.20.3 source-map-support: 0.5.21 - optional: true test-exclude@7.0.1: dependencies: @@ -3712,6 +4306,8 @@ snapshots: dependencies: typescript: 5.8.3 + tslib@2.8.1: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -3732,6 +4328,8 @@ snapshots: urlpattern-polyfill@4.0.3: {} + util-deprecate@1.0.2: {} + validate-npm-package-name@4.0.0: dependencies: builtins: 5.1.0 @@ -3757,11 +4355,23 @@ snapshots: - tsx - yaml + vite-plugin-minify@2.1.0(vite@6.3.5(@types/node@22.15.23)(sass@1.89.0)(terser@5.40.0)): + dependencies: + '@types/html-minifier-terser': 7.0.2 + html-minifier-terser: 7.2.0 + vite: 6.3.5(@types/node@22.15.23)(sass@1.89.0)(terser@5.40.0) + vite-plugin-node-csp@0.1.3(vite@6.3.5(@types/node@22.15.23)(sass@1.89.0)(terser@5.40.0)): dependencies: '@miniflare/html-rewriter': 2.14.4 vite: 6.3.5(@types/node@22.15.23)(sass@1.89.0)(terser@5.40.0) + vite-plugin-singlefile@2.2.0(rollup@4.41.1)(vite@6.3.5(@types/node@22.15.23)(sass@1.89.0)(terser@5.40.0)): + dependencies: + micromatch: 4.0.8 + rollup: 4.41.1 + vite: 6.3.5(@types/node@22.15.23)(sass@1.89.0)(terser@5.40.0) + vite@6.3.5(@types/node@22.15.23)(sass@1.89.0)(terser@5.40.0): dependencies: esbuild: 0.25.5 @@ -3842,6 +4452,10 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + which@1.3.1: + dependencies: + isexe: 2.0.0 + which@2.0.2: dependencies: isexe: 2.0.0 @@ -3867,6 +4481,11 @@ snapshots: wrappy@1.0.2: {} + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + ws@8.18.2: {} xml-name-validator@5.0.0: {} diff --git a/src/controllers/qr-code-controller.ts b/src/controllers/qr-code-controller.ts index 20bab71..14c58df 100644 --- a/src/controllers/qr-code-controller.ts +++ b/src/controllers/qr-code-controller.ts @@ -1,34 +1,36 @@ import { QRGenerator } from '../core/qr-generator'; import type { URLHashManager } from '../core/url-hash-manager'; import { FilenameSanitizer } from '../utils/filename-sanitizer'; -import { sanitizeSVG, sanitizeRemoveSVGStyles } from '../utils/svg-sanitizer'; export interface QRCodeControllerOptions { containerSelector: string; inputSelector: string; qrGenerator: QRGenerator; urlHashManager: URLHashManager; + debounceDelay?: number; } export interface QRCodeControllerElements { container: HTMLDivElement; - input: HTMLInputElement; + input: HTMLTextAreaElement; } export class QRCodeController { private elements: QRCodeControllerElements; private qrGenerator: QRGenerator; private urlHashManager: URLHashManager; - private unstyledSVGContent: string | null = null; private styledSVGContent: string | null = null; private abortController: AbortController | null = null; + private debounceDelay: number = 300; // Default debounce delay + private debounceTimeout: number | null = null; constructor(options: QRCodeControllerOptions) { this.qrGenerator = options.qrGenerator; this.urlHashManager = options.urlHashManager; + this.debounceDelay = options.debounceDelay ?? this.debounceDelay; const container = document.querySelector(options.containerSelector); - const input = document.querySelector(options.inputSelector); + const input = document.querySelector(options.inputSelector); if (!container || !input) { throw new Error('Required DOM elements not found'); @@ -47,12 +49,11 @@ export class QRCodeController { // Generate initial QR code await this.updateQRCode(); - - // Setup event listeners - this.setupEventListeners(); } catch (error) { this.handleError(error); } + + this.setupEventListeners(); } private initializeHash(): void { @@ -84,19 +85,25 @@ export class QRCodeController { } private async handleInputChange(event: Event): Promise { - const input = event.target as HTMLInputElement; - const value = input.value.trim(); + const input = event.target as HTMLTextAreaElement; + const value = input.value; - try { - if (value === '') { - this.urlHashManager.removeHash(); - } else { - this.urlHashManager.setHash(value); - } - await this.updateQRCode(); - } catch (error) { - this.handleError(error); + if (this.debounceTimeout) { + window.clearTimeout(this.debounceTimeout); } + + this.debounceTimeout = window.setTimeout(async () => { + try { + if (value === '') { + this.urlHashManager.removeHash(); + } else { + this.urlHashManager.setHash(value); + } + await this.updateQRCode(); + } catch (error) { + this.handleError(error); + } + }, this.debounceDelay); } private async handleHashChange(): Promise { @@ -113,8 +120,7 @@ export class QRCodeController { try { const rawSVG = await this.qrGenerator.generateSVG(text); - this.styledSVGContent = sanitizeSVG(rawSVG); - this.unstyledSVGContent = sanitizeRemoveSVGStyles(this.styledSVGContent); + this.styledSVGContent = rawSVG; this.renderQRCode(); } catch (error) { throw new Error( @@ -124,7 +130,7 @@ export class QRCodeController { } private renderQRCode(): void { - if (!this.unstyledSVGContent || !this.styledSVGContent) return; + if (!this.styledSVGContent) return; // Clear container this.elements.container.innerHTML = ''; @@ -143,7 +149,7 @@ export class QRCodeController { // Parse SVG and append to link const parser = new DOMParser(); - const doc = parser.parseFromString(this.unstyledSVGContent, 'image/svg+xml'); + const doc = parser.parseFromString(this.styledSVGContent, 'image/svg+xml'); const svgElement = doc.documentElement as unknown as SVGElement; // Add accessible label to SVG diff --git a/src/core/qr-generator.ts b/src/core/qr-generator.ts index 6f84b20..9bb840b 100644 --- a/src/core/qr-generator.ts +++ b/src/core/qr-generator.ts @@ -6,6 +6,7 @@ export class QRGenerator { return new QRCode({ content: text, container: 'svg-viewbox', + xmlDeclaration: false, join: true, }).svg(); } catch (error) { diff --git a/src/main.ts b/src/main.ts index 577f36c..d2161ee 100644 --- a/src/main.ts +++ b/src/main.ts @@ -42,15 +42,21 @@ async function initializeApp(): Promise { } catch (error) { console.error('Failed to initialize QR Code application:', error); - // Show error in UI const container = document.querySelector('#qr-code'); if (container) { - container.innerHTML = ` -
-

Failed to initialize the application.

-

Please refresh the page and try again.

-
- `; + const errorDiv = document.createElement('div'); + errorDiv.className = 'error-message card'; + + const errorText = document.createElement('p'); + errorText.textContent = 'Failed to initialize the application.'; + + const instructionText = document.createElement('p'); + instructionText.textContent = 'Please refresh the page and try again.'; + + errorDiv.appendChild(errorText); + errorDiv.appendChild(instructionText); + + container.appendChild(errorDiv); } } } diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..bea116b --- /dev/null +++ b/src/style.css @@ -0,0 +1,200 @@ +:root { + font-family: 'Courier New', Courier, monospace; + line-height: 1.5; + font-weight: 400; + font-size: 16px; + text-align: center; + + color-scheme: light dark; + color: rgb(255 255 255 / 87%); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizelegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + display: flex; + flex-direction: column; + place-items: center; + min-height: 100vh; +} + +#app { + max-width: 600px; + width: 100%; + padding: 2rem; + flex: 1; + + display: flex; + flex-direction: column; + gap: 2rem; + align-items: center; + justify-content: center; + text-align: center; +} + +#qr-code { + width: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.qr-code-link, +.qr-code-link svg { + width: 100%; + display: block; + border-radius: 8px; +} + +.qr-code-link { + transition: box-shadow 0.3s ease; +} + +.qr-code-link:hover { + box-shadow: 0 4px 8px rgb(0 0 0 / 20%); +} + +.qr-code-link:active { + box-shadow: 0 2px 4px rgb(0 0 0 / 30%); +} + +.qr-code-link:focus-visible { + box-shadow: 0 4px 8px rgb(0 0 0 / 30%); +} + +.qr-code-link:focus { + outline: 2px solid rgb(0 0 0 / 60%); + outline-offset: -2px; +} + +form { + width: 100%; +} + +label { + display: block; + margin-bottom: 0.5em; + font-size: 1.125em; +} + +#input-text { + width: 100%; + padding: 0.5em; + font-size: 1em; + font-family: inherit; + border-radius: 8px; + border: 1px solid rgb(255 255 255 / 20%); + background-color: #222; + color: inherit; + box-shadow: inset 0 1px 2px rgb(0 0 0 / 10%); + transition: + border-color 0.3s ease, + box-shadow 0.3s ease; + resize: vertical; + min-height: 100px; + field-sizing: content; +} + +#input-text:focus { + border-color: rgb(255 255 255 / 60%); + box-shadow: inset 0 1px 2px rgb(0 0 0 / 20%); + outline: 2px solid rgb(255 255 255 / 40%); + outline-offset: -2px; +} + +#input-text::placeholder { + color: rgb(255 255 255 / 60%); +} + +#input-text:focus::placeholder { + color: rgb(255 255 255 / 40%); +} + +footer a { + color: rgb(255 255 255 / 60%); + text-decoration: none; + transition: color 0.3s ease; +} + +footer a:hover, +footer a:focus { + color: rgb(255 255 255 / 87%); + outline: 2px solid rgb(255 255 255 / 40%); + outline-offset: 2px; +} + +footer { + padding: 1em; + color: rgb(255 255 255 / 60%); +} + +.card { + border-radius: 8px; + background-color: #fff; +} + +.error-message { + color: #ff4d4d; + display: flex; + align-items: center; + justify-content: center; + padding: 2em; + aspect-ratio: 1 / 1; +} + +.preload-spacer { + width: 100%; + height: 0; + padding-bottom: 100%; /* Maintain aspect ratio */ +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #fff; + } + + body { + color: #242424; + background-color: #fff; + } + + footer a { + color: #242424; + } + + footer a:hover, + footer a:focus { + color: #213547; + outline: 2px solid rgb(0 0 0 / 60%); + outline-offset: 2px; + } + + footer { + color: #242424; + } + + #input-text { + border: 1px solid #ccc; + box-shadow: inset 0 1px 2px rgb(0 0 0 / 10%); + background-color: #f9f9f9; + color: #242424; + } + + #input-text:focus { + border-color: rgb(0 0 0 / 60%); + box-shadow: inset 0 1px 2px rgb(0 0 0 / 20%); + outline: 2px solid rgb(0 0 0 / 40%); + outline-offset: -2px; + } +} diff --git a/src/utils/svg-sanitizer.ts b/src/utils/svg-sanitizer.ts deleted file mode 100644 index 62007e6..0000000 --- a/src/utils/svg-sanitizer.ts +++ /dev/null @@ -1,16 +0,0 @@ -import DOMPurify from 'dompurify'; - -export function sanitizeSVG(svgContent: string): string { - // Sanitize SVG content to prevent XSS attacks - return DOMPurify.sanitize(svgContent, { - USE_PROFILES: { svg: true, svgFilters: true }, - }); -} - -export function sanitizeRemoveSVGStyles(svgContent: string): string { - // Remove SVG style attributes that trip up Content-Security-Policy - return DOMPurify.sanitize(svgContent, { - USE_PROFILES: { svg: true, svgFilters: true }, - FORBID_ATTR: ['style'], - }); -} diff --git a/vite.config.ts b/vite.config.ts index d858c3f..88fdd75 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,13 +1,22 @@ import { defineConfig } from 'vite'; import { generateCspPlugin } from 'vite-plugin-node-csp'; +import { viteSingleFile } from 'vite-plugin-singlefile'; +import { ViteMinifyPlugin } from 'vite-plugin-minify'; export default defineConfig({ plugins: [ + viteSingleFile(), + ViteMinifyPlugin({}), generateCspPlugin({ algorithm: 'sha256', policy: { 'default-src': ["'none'"], 'img-src': ["'self'", 'data:'], + 'style-src': [ + "'sha256-g6yslBJfG81ksfxPODeMjl6k1k+01SGmdMDGZnzUdw8='", + "'sha256-pNew6JVTI7o7/vyDz1vjbfN+ELdoCcdPQhWEliKkywA='", + "'unsafe-hashes'", + ], }, }), ],