diff --git a/.gitattributes b/.gitattributes index dfe0770..903d9d2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,4 @@ # Auto detect text files and perform LF normalization * text=auto + +pre-commit eol=lf diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..4c4f037 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,32 @@ +name: Lint + +on: + push: + pull_request: + +permissions: + contents: read + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: '24' + cache: 'npm' + + - name: Install dependencies + run: npm install + + - parallel: + - name: ESLint + run: npm run lint:js + + - name: Stylelint + run: npm run lint:css diff --git a/.gitignore b/.gitignore index 1503da4..3bb52ea 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ /.vite/ /.coverage/ +.eslintcache +.stylelintcache + # Log files *.log npm-debug.log* diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 18b359c..4cf7191 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,6 +32,15 @@ If you encounter a bug, graphical glitch, or something isn't working as expected **Create a Pull Request:** Do you have a feature you would like added, or a task that you would like added that could benefit other Tenno? Fork the repository, and create a pull request! +### Coding Style + +This project uses [ESLint](https://eslint.org/) and [Stylelint](https://stylelint.io/) to enforce a consistent coding style. +A GitHub workflow will check your code automatically when you push changes or create a pull request. +You can also run the checks locally with `npm run lint`, and [a git pre-commit hook is provided](./pre-commit) that you can install. +Extensions may also be available for your editor. + +### AI Policy + You must disclose any use of AI that went in to preparing your pull request by **both**: 1. Marking the appropriate box in the pull request template, *and* diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..407d459 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,134 @@ +import js from "@eslint/js"; +import globals from "globals"; +import json from "@eslint/json"; +import css from "@eslint/css"; +import { defineConfig } from "eslint/config"; +import stylistic from "@stylistic/eslint-plugin"; + +export default defineConfig([ + { ignores: [".coverage/**/*"] }, + { + linterOptions: { + reportUnusedInlineConfigs: "warn", + }, + }, + { + files: ["sources/**/*.{js,mjs,cjs}", "*.config.*js"], + plugins: { js }, + languageOptions: { + globals: globals.browser, + }, + extends: [ + "js/recommended", + stylistic.configs.customize({ + indent: 4, + semi: true, + quotes: "double", + jsx: false, + arrowParens: true, + quoteProps: "consistent", + }), + ], + rules: { + // "important" stuff + "curly": ["error", "all"], + "eqeqeq": ["error", "always"], + "no-var": "error", + "prefer-const": "error", + "radix": "error", + // niche stuff + "array-callback-return": "error", + "no-duplicate-imports": "error", + "no-self-compare": "error", + "no-template-curly-in-string": "warn", + "no-unreachable-loop": "error", + "no-use-before-define": ["error", "nofunc"], + "block-scoped-var": "error", + "consistent-return": "error", + "default-param-last": "error", + "dot-notation": "warn", + "func-style": ["error", "declaration"], + "no-empty-function": "error", + "no-extend-native": "error", + "no-labels": "error", + "no-implicit-coercion": "error", + "no-lone-blocks": "error", + "no-lonely-if": "error", + "no-new-wrappers": "error", + "no-object-constructor": "error", + "no-proto": "error", + "no-return-assign": ["error", "always"], + "no-sequences": ["error", { "allowInParentheses": false }], + "no-shadow": "error", + "no-throw-literal": "error", + "no-unused-expressions": "error", + "no-useless-concat": "error", + "no-useless-rename": "error", + "no-void": "error", + "prefer-arrow-callback": "error", + "prefer-numeric-literals": "error", + "unicode-bom": "error", + // eval + "no-eval": "error", + "no-implied-eval": "error", + "no-new-func": "error", + "no-script-url": "error", + // complexity + "complexity": ["warn", 20], + "id-length": ["warn", { "min": 1, "max": 30 }], + "max-depth": ["warn", 4], + "max-lines-per-function": ["warn", { + "max": 60, + "skipBlankLines": true, + "skipComments": true, + }], + "max-nested-callbacks": ["warn", 4], + "max-params": ["warn", 4], + // stylistic + "@stylistic/quotes": ["error", "double", { "avoidEscape": true }], + "@stylistic/brace-style": ["error", "1tbs", { "allowSingleLine": true }], + "@stylistic/no-multiple-empty-lines": ["error", { "max": 2 }], + "@stylistic/max-statements-per-line": ["error", { "max": 2 }], + "@stylistic/object-curly-spacing": ["error", "always", { "emptyObjects": "never" }], + }, + }, + { + files: ["sources/tests/*.js", "*.config.*js"], + languageOptions: { + globals: globals.node, + }, + }, + { + files: ["sources/**/*.json"], + plugins: { json }, + language: "json/json", + extends: ["json/recommended"], + }, + { + files: ["sources/**/*.css"], + plugins: { css }, + language: "css/css", + extends: ["css/recommended"], + languageOptions: { + tolerant: true, + }, + rules: { + /* allowUnknownVariables suppresses errors from variables defined in other files. + Stylelint does proper checking for this kind of error with its `referenceFiles` feature. */ + "css/no-invalid-properties": ["error", { "allowUnknownVariables": true }], + "css/use-baseline": ["warn", { + "available": "widely", + "allowProperties": [ + "backdrop-filter", // baseline 2024 + ], + "allowPropertyValues": { + "background-attachment": ["fixed"], // only unavailable in iOS + }, + "allowAtRules": [ + "starting-style", // baseline 2024 + ], + }], + "css/no-important": "warn", + }, + }, +]); diff --git a/package-lock.json b/package-lock.json index 07d1c85..646611c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,21 @@ "version": "5.3", "license": "GPL-3.0-or-later", "devDependencies": { + "@eslint/css": "^1.4.0", + "@eslint/js": "^10.0.1", + "@eslint/json": "^2.0.1", + "@stylistic/eslint-plugin": "^5.10.0", + "@stylistic/stylelint-config": "^5.0.0", "@vitest/coverage-v8": "^4.1.5", "@vitest/ui": "^4.1.5", "ajv": "^8.20.0", "ajv-formats": "^3.0.1", + "eslint": "^10.8.0", + "globals": "^17.8.0", "jsdom": "^29.1.1", "sanitize.css": "^13.0.0", + "stylelint": "^17.14.1", + "stylelint-config-standard": "^40.0.0", "vite": "^8.0.16", "vite-plugin-singlefile": "^2.3.3", "vitest": "^4.1.5" @@ -71,6 +80,28 @@ "dev": true, "license": "MIT" }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -82,9 +113,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -144,6 +175,67 @@ "specificity": "bin/cli.js" } }, + "node_modules/@cacheable/memory": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz", + "integrity": "sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/utils": "^2.5.0", + "@keyv/bigmap": "^1.3.1", + "hookified": "^1.15.1", + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/memory/node_modules/@keyv/bigmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.4.0", + "hookified": "^1.15.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/memory/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/@cacheable/utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.5.0.tgz", + "integrity": "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.5.1", + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/utils/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, "node_modules/@csstools/color-helpers": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", @@ -165,9 +257,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.0.tgz", - "integrity": "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz", + "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==", "dev": true, "funding": [ { @@ -240,9 +332,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.3.tgz", - "integrity": "sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz", + "integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==", "dev": true, "funding": [ { @@ -284,6 +376,76 @@ "node": ">=20.19.0" } }, + "node_modules/@csstools/media-query-list-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-5.0.0.tgz", + "integrity": "sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/selector-resolve-nested": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-4.0.1.tgz", + "integrity": "sha512-j3vdQu0XwLME5qOTWxm8cnmvsf423R2YL6DbKklCHZwkDm7UdKNu6RPlw4REIJhSlKBICY3B70/7QZdicLqZgg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.1.1" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-6.0.0.tgz", + "integrity": "sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.1.1" + } + }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -318,6 +480,186 @@ "tslib": "^2.4.0" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/css": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@eslint/css/-/css-1.4.0.tgz", + "integrity": "sha512-OnRNKgnbX+tufPuZc2kOJS+v1iIARvfJHRNEAVwN77DBpojl+rGjEP8NKTL6pEZ7BR+HtwIuSSdrymEFlANGxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "@eslint/css-tree": "^4.0.4", + "@eslint/plugin-kit": "^0.7.2" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/css-tree": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@eslint/css-tree/-/css-tree-4.0.5.tgz", + "integrity": "sha512-iPmijIAq4hlIJB86PYmY/fcZORHtjphSqICDbwuw32A/JmkhZQ/K/6TjHE03zqf3n5yABpVcbRAMG8Mi9ojy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.29.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/css-tree/node_modules/mdn-data": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.29.0.tgz", + "integrity": "sha512-pVxQFCcaYUEAH853+v7yoI/qzhxXSq1bTb9obMYGYAN1c3Hen+XDCEvr296XhstrwlSTNgOR7mCSD4JPjbJe5A==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@eslint/json/-/json-2.0.1.tgz", + "integrity": "sha512-Thz2j92ceUF3Bq/0TuWb3MWn3Z+Cwc8k5ptF0fakl2D4Mp8mSx07Xr1aQM4R5NoihzarWUdxfOmQ8DesGy4jOg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanwhocodes/momoa": "^3.3.10", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, "node_modules/@exodus/bytes": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", @@ -336,6 +678,82 @@ } } }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/momoa": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-3.3.10.tgz", + "integrity": "sha512-KWiFQpSAqEIyrTXko3hFNLeQvSK8zXlJQzhhxsyVn58WFRYXST99b3Nqnu+ttOtjds2Pl2grUHGpe2NzhPynuQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -363,6 +781,13 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true, + "license": "MIT" + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", @@ -382,20 +807,58 @@ "@emnapi/runtime": "^1.7.1" } }, - "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@polka/url": { - "version": "1.0.0-next.29", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", - "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.133.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", + "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", "dev": true, "license": "MIT" }, @@ -681,12 +1144,112 @@ "dev": true, "license": "MIT" }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true }, + "node_modules/@stylistic/eslint-plugin": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.10.0.tgz", + "integrity": "sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/types": "^8.56.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.0.0 || ^10.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/stylelint-config": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@stylistic/stylelint-config/-/stylelint-config-5.0.0.tgz", + "integrity": "sha512-AW6S27wEm4DzB+uOZMvRkONIu0ba50VRbFte7qYJgGP4dacS8kAuj8HZJ+s+8kE4GK3XXBcSKLb75a+NfJlbiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@stylistic/stylelint-plugin": "^5.1.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "stylelint": "^17.6.0" + } + }, + "node_modules/@stylistic/stylelint-plugin": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-5.2.1.tgz", + "integrity": "sha512-BMRWJVtO16uADJmKcGi6mreei/QYdOviABXuaCeHEc3T3pxo4wFYYSvnxIoyiPVfDJqX+DxQdnGkuc6KxsRaSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "@csstools/media-query-list-parser": "^5.0.0", + "postcss": "^8.5.8", + "postcss-selector-parser": "^7.1.1", + "postcss-value-parser": "^4.2.0", + "style-search": "^0.1.0" + }, + "peerDependencies": { + "stylelint": "^17.6.0" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", @@ -714,12 +1277,40 @@ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/types": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@vitest/coverage-v8": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.5.tgz", @@ -907,6 +1498,29 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/ajv": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", @@ -942,6 +1556,42 @@ } } }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -973,6 +1623,26 @@ "@types/estree": "^1.0.0" } }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/bidi-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", @@ -983,6 +1653,19 @@ "require-from-string": "^2.0.2" } }, + "node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -996,6 +1679,40 @@ "node": ">=8" } }, + "node_modules/cacheable": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.5.0.tgz", + "integrity": "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/memory": "^2.2.0", + "@cacheable/utils": "^2.5.0", + "hookified": "^1.15.0", + "keyv": "^5.6.0", + "qified": "^0.10.1" + } + }, + "node_modules/cacheable/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", @@ -1005,12 +1722,91 @@ "node": ">=18" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true, + "license": "MIT" + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/cosmiconfig": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-functions-list": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.3.3.tgz", + "integrity": "sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/css-tree": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", @@ -1025,6 +1821,19 @@ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/data-urls": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", @@ -1039,6 +1848,24 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/decimal.js": { "version": "10.6.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", @@ -1046,6 +1873,13 @@ "dev": true, "license": "MIT" }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -1056,28 +1890,291 @@ "node": ">=8" } }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-module-lexer": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "dev": true }, - "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fast-deep-equal": { + "node_modules/eslint": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz", + "integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.7.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-uri": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", @@ -1095,6 +2192,26 @@ ], "license": "BSD-3-Clause" }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1119,6 +2236,19 @@ "dev": true, "license": "MIT" }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -1132,6 +2262,37 @@ "node": ">=8" } }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/flatted": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", @@ -1154,103 +2315,392 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/html-encoding-sniffer": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", - "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@exodus/bytes": "^1.6.0" + "is-glob": "^4.0.3" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + "node": ">=10.13.0" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", "dev": true, "license": "MIT", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, "engines": { - "node": ">=0.12.0" + "node": ">=6" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, - "license": "MIT" + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "node_modules/globals": { + "version": "17.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.8.0.tgz", + "integrity": "sha512-Zz/LMDZScFmkakeL2cTHzf+PbWKdpU3uclqkZT7TjDG58j5WPt0PpA+n9uPI24fZtlw07q0OtEi84K+umsRzqQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "node_modules/globby": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-16.2.2.tgz", + "integrity": "sha512-NLvV9ubZ6NDsJaOpKPy3cQeJpKi9DcWiyCiFUpJPA0YihRqiE6RWaLUmgNNPr8MgPpLZjnBjSmou7uZBRJv9wA==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" + "@sindresorhus/merge-streams": "^4.0.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.5", + "is-path-inside": "^4.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.4.0" }, "engines": { - "node": ">=10" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "node_modules/globby/node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 4" } }, - "node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", "dev": true, "license": "MIT" }, - "node_modules/jsdom": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", - "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hashery": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz", + "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.15.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/hookified": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-tags": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-5.1.0.tgz", + "integrity": "sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1288,6 +2738,20 @@ } } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -1295,6 +2759,47 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", @@ -1568,6 +3073,36 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, "node_modules/lru-cache": { "version": "11.3.6", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", @@ -1615,6 +3150,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mathml-tag-names": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-4.0.0.tgz", + "integrity": "sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/mdn-data": { "version": "2.27.1", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", @@ -1622,21 +3168,44 @@ "dev": true, "license": "CC0-1.0" }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/meow": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-14.1.0.tgz", + "integrity": "sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==", "dev": true, "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", @@ -1649,6 +3218,22 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", @@ -1659,10 +3244,17 @@ "node": ">=10" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", "dev": true, "funding": [ { @@ -1678,6 +3270,23 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/obug": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", @@ -1688,6 +3297,88 @@ "https://opencollective.com/debug" ] }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parse5": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", @@ -1714,6 +3405,26 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1721,197 +3432,693 @@ "dev": true, "license": "ISC" }, - "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-safe-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", + "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz", + "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qified": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz", + "integrity": "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^2.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/qified/node_modules/hookified": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz", + "integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==", + "dev": true, + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rolldown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", + "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.133.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.3", + "@rolldown/binding-darwin-arm64": "1.0.3", + "@rolldown/binding-darwin-x64": "1.0.3", + "@rolldown/binding-freebsd-x64": "1.0.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.3", + "@rolldown/binding-linux-arm64-musl": "1.0.3", + "@rolldown/binding-linux-ppc64-gnu": "1.0.3", + "@rolldown/binding-linux-s390x-gnu": "1.0.3", + "@rolldown/binding-linux-x64-gnu": "1.0.3", + "@rolldown/binding-linux-x64-musl": "1.0.3", + "@rolldown/binding-openharmony-arm64": "1.0.3", + "@rolldown/binding-wasm32-wasi": "1.0.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.3", + "@rolldown/binding-win32-x64-msvc": "1.0.3" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/sanitize.css": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", + "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true + }, + "node_modules/string-width": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", + "dev": true, + "license": "ISC" + }, + "node_modules/stylelint": { + "version": "17.14.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.14.1.tgz", + "integrity": "sha512-xVQwyiuxALUBNB2fBe0tmNemg9KqLtdj3T64mioFDar79B2cU8LIyz+3KL6LdiHs9NkeNfwxpKSaIVOY8f112g==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "@csstools/css-calc": "^3.2.1", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-syntax-patches-for-csstree": "^1.1.6", + "@csstools/css-tokenizer": "^4.0.0", + "@csstools/media-query-list-parser": "^5.0.0", + "@csstools/selector-resolve-nested": "^4.0.0", + "@csstools/selector-specificity": "^6.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.2", + "css-functions-list": "^3.3.3", + "css-tree": "^3.2.1", + "debug": "^4.4.3", + "fast-glob": "^3.3.3", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^11.1.5", + "global-modules": "^2.0.0", + "globby": "^16.2.1", + "globjoin": "^0.1.4", + "html-tags": "^5.1.0", + "ignore": "^7.0.5", + "import-meta-resolve": "^4.2.0", + "mathml-tag-names": "^4.0.0", + "meow": "^14.1.0", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.5.16", + "postcss-safe-parser": "^7.0.1", + "postcss-selector-parser": "^7.1.4", + "postcss-value-parser": "^4.2.0", + "string-width": "^8.2.1", + "supports-hyperlinks": "^4.5.0", + "svg-tags": "^1.0.0", + "table": "^6.9.0", + "write-file-atomic": "^7.0.1" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "bin": { + "stylelint": "bin/stylelint.mjs" + }, + "engines": { + "node": ">=20.19.0" } }, - "node_modules/postcss": { - "version": "8.5.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", - "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", + "node_modules/stylelint-config-recommended": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-18.0.0.tgz", + "integrity": "sha512-mxgT2XY6YZ3HWWe3Di8umG6aBmWmHTblTgu/f10rqFXnyWxjKWwNdjSWkgkwCtxIKnqjSJzvFmPT5yabVIRxZg==", "dev": true, "funding": [ { "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "url": "https://opencollective.com/stylelint" }, { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "stylelint": "^17.0.0" + } + }, + "node_modules/stylelint-config-standard": { + "version": "40.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-40.0.0.tgz", + "integrity": "sha512-EznGJxOUhtWck2r6dJpbgAdPATIzvpLdK9+i5qPd4Lx70es66TkBPljSg4wN3Qnc6c4h2n+WbUrUynQ3fanjHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" }, { "type": "github", - "url": "https://github.com/sponsors/ai" + "url": "https://github.com/sponsors/stylelint" } ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "stylelint-config-recommended": "^18.0.0" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=20.19.0" + }, + "peerDependencies": { + "stylelint": "^17.0.0" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz", + "integrity": "sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "flat-cache": "^6.1.23" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "node_modules/stylelint/node_modules/flat-cache": { + "version": "6.1.23", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.23.tgz", + "integrity": "sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cacheable": "^2.5.0", + "flatted": "^3.4.2", + "hookified": "^1.15.0" + } + }, + "node_modules/stylelint/node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 4" } }, - "node_modules/rolldown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", - "@rolldown/pluginutils": "^1.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" + "has-flag": "^4.0.0" }, "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" + "node": ">=8" } }, - "node_modules/sanitize.css": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", - "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "node_modules/supports-hyperlinks": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.5.0.tgz", + "integrity": "sha512-ZW2OvfeCXrNTbLakPUzjQG922EeGCOteFSVoek5DKStTh898wf7zgtuFlzQN8HfZCxC3Eh02yJVrRW51hADf+w==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "xmlchars": "^2.2.0" + "has-flag": "^5.0.1", + "supports-color": "^10.2.2" }, "engines": { - "node": ">=v12.22.7" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" } }, - "node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz", + "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "engines": { + "node": ">=12" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", "dev": true }, - "node_modules/sirv": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", - "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=18" + "node": ">=10.0.0" } }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true - }, - "node_modules/std-env": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", - "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", - "dev": true - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } }, "node_modules/tinybench": { "version": "2.9.0", @@ -2031,6 +4238,19 @@ "license": "0BSD", "optional": true }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/undici": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", @@ -2041,6 +4261,36 @@ "node": ">=20.18.1" } }, + "node_modules/unicorn-magic": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz", + "integrity": "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/vite": { "version": "8.0.16", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", @@ -2284,6 +4534,22 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -2300,6 +4566,29 @@ "node": ">=8" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/write-file-atomic": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-7.0.1.tgz", + "integrity": "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==", + "dev": true, + "license": "ISC", + "dependencies": { + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", @@ -2316,6 +4605,19 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true, "license": "MIT" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index c7d0be7..e6f15c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "task-checklist", - "version": "5.3", + "version": "5.3.1", "description": "A Warframe Task Checklist to track daily, weekly, and other tasks. Built with HTML, CSS, and vanilla JavaScript, processed with Vite.", "type": "module", "scripts": { @@ -9,7 +9,10 @@ "build-release": "vite build --mode release", "preview": "vite preview", "test": "vitest", - "test:ui": "vitest --ui --open=false --coverage" + "test:ui": "vitest --ui --open=false --coverage", + "lint": "npm run lint:js && npm run lint:css", + "lint:js": "eslint --concurrency=auto --cache", + "lint:css": "stylelint \"sources/**/*.css\" --cache" }, "repository": { "type": "git", @@ -29,12 +32,21 @@ }, "homepage": "https://warframetools.com/Task-Checklist/", "devDependencies": { + "@eslint/css": "^1.4.0", + "@eslint/js": "^10.0.1", + "@eslint/json": "^2.0.1", + "@stylistic/eslint-plugin": "^5.10.0", + "@stylistic/stylelint-config": "^5.0.0", "@vitest/coverage-v8": "^4.1.5", "@vitest/ui": "^4.1.5", "ajv": "^8.20.0", "ajv-formats": "^3.0.1", + "eslint": "^10.8.0", + "globals": "^17.8.0", "jsdom": "^29.1.1", "sanitize.css": "^13.0.0", + "stylelint": "^17.14.1", + "stylelint-config-standard": "^40.0.0", "vite": "^8.0.16", "vite-plugin-singlefile": "^2.3.3", "vitest": "^4.1.5" diff --git a/pre-commit b/pre-commit new file mode 100644 index 0000000..6efd652 --- /dev/null +++ b/pre-commit @@ -0,0 +1,50 @@ +#!/bin/bash +# This is a git pre-commit hook to check for linting errors. +# Copy or symlink this file into .git/hooks/ to install it. + +if [[ -t 1 && -z "$NO_COLOR" ]]; then + color="--color" +else + color="--no-color" +fi + +if [[ pre-commit -nt .git/hooks/pre-commit ]]; then + echo "The pre-commit hook version distributed by the repository is newer than the installed version. You may wish to update." + stat -c "repository: %y : %n" pre-commit + stat -c "installed: %y : %n" .git/hooks/pre-commit + echo +fi + +# Redirect output to stderr. +exec 1>&2 + +# Run commands in the background and redirect their output to new file descriptors +exec 3< <(npm run -s test -- run --silent --reporter=minimal --bail=1 $color 2>&1); test_pid=$! +exec 4< <(npm run -s lint:js -- $color 2>&1); js_pid=$! +exec 5< <(npm run -s lint:css -- $color 2>&1); css_pid=$! + +# Wait for commands to complete and capture their exit codes +wait $test_pid; test_exit=$? +wait $js_pid; js_exit=$? +wait $css_pid; css_exit=$? + +if [[ $test_exit != 0 || $js_exit != 0 || $css_exit != 0 ]]; then + msg="Fix errors before committing, or skip this check with --no-verify" + echo -e "$msg\n" # this message is for the Commit button in the vscode ui + if [[ $test_exit != 0 ]]; then + echo "----- Vitest report - (exit code $test_exit) --------------" + cat <&3 + echo -e "Only the first failing test is reported. Run \`npm run test\` to check for more.\n" + fi + if [[ $js_exit != 0 ]]; then + echo "----- ESLint report - (exit code $js_exit) --------------" + cat <&4 + fi + if [[ $css_exit != 0 ]]; then + echo "----- Stylelint report - (exit code $css_exit) -----------" + cat <&5 + fi + echo -e "--------------------------------------------------\n" + echo "$msg" # this message is for if you're committing on a terminal + exit 1 +fi diff --git a/sources/css/critical.css b/sources/css/critical.css index 26e9782..b29be6f 100644 --- a/sources/css/critical.css +++ b/sources/css/critical.css @@ -1,37 +1,37 @@ /* @font-face rules for self-hosted Inter font */ @font-face { - font-family: 'Inter'; + font-family: "Inter"; font-style: normal; font-weight: 400; font-display: swap; - src: url('/fonts/inter-v18-latin-regular.woff2') format('woff2'); + src: url("/fonts/inter-v18-latin-regular.woff2") format("woff2"); } @font-face { - font-family: 'Inter'; + font-family: "Inter"; font-style: normal; font-weight: 500; font-display: swap; - src: url('/fonts/inter-v18-latin-500.woff2') format('woff2'); + src: url("/fonts/inter-v18-latin-500.woff2") format("woff2"); } @font-face { - font-family: 'Inter'; + font-family: "Inter"; font-style: normal; font-weight: 600; font-display: swap; - src: url('/fonts/inter-v18-latin-600.woff2') format('woff2'); + src: url("/fonts/inter-v18-latin-600.woff2") format("woff2"); } @font-face { - font-family: 'Inter'; + font-family: "Inter"; font-style: normal; font-weight: 700; font-display: swap; - src: url('/fonts/inter-v18-latin-700.woff2') format('woff2'); + src: url("/fonts/inter-v18-latin-700.woff2") format("woff2"); } /* --- Global Theme Variables (Critical for initial paint) --- */ :root { /* Default: Dark Mode */ - --bg-content: rgba(31, 41, 55, 0.95); + --bg-content: rgb(31 41 55 / 95%); --text-primary: #f3f4f6; --text-secondary: #9ca3af; --text-muted: #6b7280; @@ -41,27 +41,27 @@ --border-color: #4b5563; --checkbox-check-color: var(--text-primary); --checkbox-lock-color: var(--text-primary); - --checkbox-bg: rgba(255, 255, 255, 0.1); + --checkbox-bg: rgb(255 255 255 / 10%); --checkbox-border: var(--text-muted); --checkbox-checked-bg: var(--text-header); --checkbox-checked-border: var(--text-muted); - --checkbox-locked-bg: hsla(17, 100%, 70%, 0.15); + --checkbox-locked-bg: hsl(17deg 100% 70% / 15%); --checkbox-locked-border: var(--text-muted); --link-color: var(--text-header); --link-hover-color: var(--text-primary); --theme-toggle-border: var(--border-color); - --theme-toggle-hover-bg: rgba(255, 255, 255, 0.1); + --theme-toggle-hover-bg: rgb(255 255 255 / 10%); --theme-toggle-hover-border: var(--text-secondary); --theme-toggle-color: var(--text-secondary); - --section-header-hover-bg: rgba(255, 255, 255, 0.05); + --section-header-hover-bg: rgb(255 255 255 / 5%); --confirm-bg: #f59e0b; --confirm-hover-bg: #d97706; --confirm-text: #1f2937; --error-bg: #ef4444; --error-border: #dc2626; - --error-text: #ffffff; - --error-button-bg: rgba(255, 255, 255, 0.1); - --error-button-hover-bg: rgba(255, 255, 255, 0.2); + --error-text: white; + --error-button-bg: rgb(255 255 255 / 10%); + --error-button-hover-bg: rgb(255 255 255 / 20%); --menu-panel-bg: #272e3a; --menu-panel-border: #374151; --menu-title-color: var(--text-primary); @@ -77,7 +77,7 @@ } body.light-mode { - --bg-content: rgba(255, 255, 255, 0.95); + --bg-content: rgb(255 255 255 / 95%); --text-primary: #1f2937; --text-secondary: #4b5563; --text-muted: #6b7280; @@ -87,21 +87,21 @@ body.light-mode { --border-color: #e5e7eb; --checkbox-check-color: var(--text-primary); --checkbox-lock-color: var(--text-primary); - --checkbox-bg: rgba(0, 0, 0, 0.02); + --checkbox-bg: rgb(0 0 0 / 2%); --checkbox-border: #cbd5e1; - --checkbox-locked-bg: hsla(17, 100%, 30%, 0.15); + --checkbox-locked-bg: hsl(17deg 100% 30% / 15%); --link-color: var(--text-header); --link-hover-color: var(--text-primary); --theme-toggle-border: #d1d5db; - --theme-toggle-hover-bg: rgba(0, 0, 0, 0.05); + --theme-toggle-hover-bg: rgb(0 0 0 / 5%); --theme-toggle-hover-border: var(--text-muted); --theme-toggle-color: var(--text-muted); - --section-header-hover-bg: rgba(0, 0, 0, 0.03); + --section-header-hover-bg: rgb(0 0 0 / 3%); --error-bg: #fee2e2; --error-border: #f87171; --error-text: #b91c1c; - --error-button-bg: rgba(0, 0, 0, 0.05); - --error-button-hover-bg: rgba(0, 0, 0, 0.1); + --error-button-bg: rgb(0 0 0 / 5%); + --error-button-hover-bg: rgb(0 0 0 / 10%); --menu-panel-bg: #f9fafb; --menu-panel-border: var(--border-color); --menu-title-color: var(--text-primary); @@ -138,7 +138,7 @@ body.light-mode { /* --- Base Body Styles --- */ body { - font-family: 'Inter', sans-serif; + font-family: "Inter", sans-serif; background-size: cover; background-position: center center; background-repeat: no-repeat; @@ -152,12 +152,12 @@ body { body::before { content: ""; position: fixed; - top: 0; left: 0; right: 0; bottom: 0; - background-color: rgba(0, 0, 0, 0.6); + inset: 0; + background-color: rgb(0 0 0 / 60%); z-index: -1; } body.light-mode::before { - background-color: rgba(255, 255, 255, 0.6); + background-color: rgb(255 255 255 / 60%); } /* --- Content Box Element Styling --- */ @@ -170,8 +170,8 @@ body.light-mode::before { margin: 2rem auto; padding: 2rem; border-radius: 0.5rem; - box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), - 0 8px 10px -6px rgb(0 0 0 / 0.1); + box-shadow: 0 20px 25px -5px rgb(0 0 0 / 10%), + 0 8px 10px -6px rgb(0 0 0 / 10%); @media (width < 48rem) { padding: 1.5rem; margin: 0 auto; @@ -230,6 +230,20 @@ h1 { margin-bottom: 0.5rem; } +.task-info-expander { + --current-cycle-svg-size: 1.125rem; + --task-info-expander-svg-mr: 0.2em; +} + +.current-cycle svg { + width: var(--current-cycle-svg-size); + height: var(--current-cycle-svg-size); +} +.info-line svg { + width: 0.9375rem; + height: 0.9375rem; +} + /* Theme switching animation */ @media (prefers-reduced-motion: no-preference) { body { transition: background-color 0.3s ease, color 0.3s ease; } @@ -259,7 +273,8 @@ h1 { .parent-task-header[aria-expanded="false"] .collapse-btn svg { transform: rotate(-90deg); } -.section-content, .subtask-collapsible { +.section-content, +.subtask-collapsible { display: grid; grid-template-rows: 1fr; overflow: hidden; @@ -267,15 +282,18 @@ h1 { padding-left: 0.5rem; } @media (prefers-reduced-motion: no-preference) { - .section-content, .subtask-collapsible { - transition: grid-template-rows 0.5s cubic-bezier(.23,1,.32,1), opacity 0.5s ease-in-out; + .section-content, + .subtask-collapsible { + transition: grid-template-rows 0.5s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.5s ease-in-out; } } -.section-content.collapsed, .subtask-collapsible.collapsed { +.section-content.collapsed, +.subtask-collapsible.collapsed { opacity: 0.5; grid-template-rows: 0fr; } -.section-content > :first-child, .subtask-collapsible > :first-child { +.section-content > :first-child, +.subtask-collapsible > :first-child { min-height: 0; } @@ -310,7 +328,7 @@ body:not(.light-mode) #sun-icon { display: none; } dialog { background-color: var(--menu-panel-bg); border: 1px solid var(--menu-panel-border); - box-shadow: 0 8px 16px rgba(0,0,0,0.2); + box-shadow: 0 8px 16px rgb(0 0 0 / 20%); border-radius: 0.5rem; } dialog > div { @@ -359,22 +377,8 @@ dialog > div { } #error-display.visible { display: flex; } #error-message { flex-grow: 1; } -#error-copy-button, #error-close-button { +#error-copy-button, +#error-close-button { cursor: pointer; flex-shrink: 0; } - - -.task-info-expander { - --current-cycle-svg-size: 1.125rem; - --task-info-expander-svg-mr: 0.2em; -} - -.current-cycle svg { - width: var(--current-cycle-svg-size); - height: var(--current-cycle-svg-size); -} -.info-line svg { - width: 0.9375rem; - height: 0.9375rem; -} diff --git a/sources/css/style.css b/sources/css/style.css index d7eca21..14ae7f6 100644 --- a/sources/css/style.css +++ b/sources/css/style.css @@ -3,11 +3,11 @@ /* --- Background Image Styles --- */ /* Paths are relative to this style.css file (e.g., if style.css is in sources/css/ and images are in sources/img/) */ -#bg-image-0 { background-image: url('../img/garuda-fortuna.webp'); } -#bg-image-1 { background-image: url('../img/gara-eidolon.webp'); } -#bg-image-2 { background-image: url('../img/heart-of-deimos-warframe.webp'); } -#bg-image-3 { background-image: url('../img/duviri.webp'); } -#bg-image-4 { background-image: url('../img/perita.webp'); } +#bg-image-0 { background-image: url("../img/garuda-fortuna.webp"); } +#bg-image-1 { background-image: url("../img/gara-eidolon.webp"); } +#bg-image-2 { background-image: url("../img/heart-of-deimos-warframe.webp"); } +#bg-image-3 { background-image: url("../img/duviri.webp"); } +#bg-image-4 { background-image: url("../img/perita.webp"); } /* Add more #bg-image-X rules if you have more images */ .daily-background-image { @@ -31,7 +31,6 @@ #checklist-container h2:hover { background-color: var(--section-header-hover-bg); } -.task-description { color: var(--text-label); } #save-info { margin-top: 1rem; @@ -43,7 +42,7 @@ margin-top: 0.25rem; } #save-status { - color: rgb(22 163 74 / 1); + color: rgb(22 163 74 / 100%); opacity: 0; font-size: 0.875rem; line-height: 1.25rem; @@ -118,6 +117,7 @@ a.git-hash-text { margin-bottom: 0.25rem; } .task-description { + color: var(--text-label); flex: 1 1 0%; margin-left: 0.5rem; cursor: pointer; @@ -160,7 +160,7 @@ a.git-hash-text { opacity: 1; } .section-is-hidden-by-user { - display: none !important; + display: none; } .hide-section-button { background-color: var(--hide-section-btn-bg); @@ -188,15 +188,15 @@ a.git-hash-text { font-variant-numeric: tabular-nums; } - /* --- Interactive Elements (Using Variables) --- */ input[type="checkbox"] { - appearance: none; -webkit-appearance: none; -moz-appearance: none; + appearance: none; width: 1.25rem; height: 1.25rem; margin: 0.125rem 0.5rem 0.125rem 0; border: 2px solid var(--checkbox-border); - border-radius: 0.25rem; cursor: pointer; + border-radius: 0.25rem; + cursor: pointer; position: relative; transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out; background-color: var(--checkbox-bg); @@ -248,36 +248,35 @@ input[type="checkbox"]:indeterminate::after { cursor: pointer; line-height: 1.5; } -/* Specific Button Colors (Default State) */ -#reset-daily-button:not(.confirming) { background-color: #3b82f6; border-color: #3b82f6; color: white;} -#reset-daily-button:not(.confirming):hover { background-color: #2563eb; border-color: #2563eb; color: white;} -#reset-weekly-button:not(.confirming) { background-color: #14b8a6; border-color: #14b8a6; color: white;} -#reset-weekly-button:not(.confirming):hover { background-color: #0d9488; border-color: #0d9488; color: white;} -#reset-button:not(.confirming) { background-color: #ef4444; border-color: #ef4444; color: white;} -#reset-button:not(.confirming):hover { background-color: #dc2626; border-color: #dc2626; color: white;} -#unhide-tasks-button:not(.confirming) { background-color: #8b5cf6; border-color: #8b5cf6; color: white; } +/* Specific Button Colors (Default State) */ /* stylelint-disable declaration-block-single-line-max-declarations, @stylistic/block-opening-brace-space-before */ +#reset-daily-button:not(.confirming) { background-color: #3b82f6; border-color: #3b82f6; color: white; } +#reset-daily-button:not(.confirming):hover { background-color: #2563eb; border-color: #2563eb; color: white; } +#reset-weekly-button:not(.confirming) { background-color: #14b8a6; border-color: #14b8a6; color: white; } +#reset-weekly-button:not(.confirming):hover { background-color: #0d9488; border-color: #0d9488; color: white; } +#reset-button:not(.confirming) { background-color: #ef4444; border-color: #ef4444; color: white; } +#reset-button:not(.confirming):hover { background-color: #dc2626; border-color: #dc2626; color: white; } +#unhide-tasks-button:not(.confirming) { background-color: #8b5cf6; border-color: #8b5cf6; color: white; } #unhide-tasks-button:not(.confirming):hover { background-color: #7c3aed; border-color: #7c3aed; color: white; } - +/* stylelint-enable declaration-block-single-line-max-declarations, @stylistic/block-opening-brace-space-before */ /* Confirmation State Styling (Applies to all reset buttons) */ .menu-btn.confirming { background-color: var(--confirm-bg); border-color: var(--confirm-bg); - color: var(--confirm-text) !important; + color: var(--confirm-text); } .menu-btn.confirming:hover { background-color: var(--confirm-hover-bg); border-color: var(--confirm-hover-bg); } - /* Theme Toggle Button Specifics (Non-critical hover states) */ -#theme-toggle-button:hover, #hamburger-button:hover { +#theme-toggle-button:hover, +#hamburger-button:hover { background-color: var(--theme-toggle-hover-bg); border-color: var(--theme-toggle-hover-border); } - /* Error Display (Non-critical hover states for buttons) */ #error-copy-button { background-color: var(--error-button-bg); @@ -290,13 +289,15 @@ input[type="checkbox"]:indeterminate::after { } #error-copy-button:hover { background-color: var(--error-button-hover-bg); } #error-close-button { - background: none; border: none; + background: none; + border: none; color: var(--error-text); - font-size: 1.5rem; line-height: 1; + font-size: 1.5rem; + line-height: 1; padding: 0 0.5rem; opacity: 0.7; } - #error-close-button:hover { opacity: 1; } +#error-close-button:hover { opacity: 1; } /* Hamburger Menu (Non-critical hover states) */ .menu-close-button:hover { @@ -340,7 +341,7 @@ dialog hr { dialog { --dialog-anim-duration: 0.15s; --dialog-anim-easing: ease-out; - transition: transform var(--dialog-anim-duration) cubic-bezier(.25,1,.5,1), + transition: transform var(--dialog-anim-duration) cubic-bezier(0.25, 1, 0.5, 1), opacity var(--dialog-anim-duration) var(--dialog-anim-easing), display var(--dialog-anim-duration) allow-discrete, overlay var(--dialog-anim-duration) allow-discrete; @@ -370,7 +371,6 @@ dialog hr { } } - /* Task Icons */ .task-icon { height: 1.5em; @@ -403,7 +403,9 @@ body:not(.light-mode) img.icon-filter { overflow: hidden; } @media (prefers-reduced-motion: no-preference) { - .task-info-expander { transition: grid-template-rows 200ms; /* https://css-tricks.com/css-grid-can-do-auto-height-transitions/ */ } + .task-info-expander { + transition: grid-template-rows 0.2s; /* https://css-tricks.com/css-grid-can-do-auto-height-transitions/ */ + } } .task-item .checked .task-info-expander { grid-template-rows: 0fr; @@ -418,6 +420,7 @@ body:not(.light-mode) img.icon-filter { font-size: 90%; color: color-mix(in hsl, var(--text-label) 50%, var(--text-secondary) 50%); margin-left: 0.5em; + /* eslint-disable-next-line css/use-baseline -- (text-indent: hanging; baseline 2026) only relevant on very narrow screens for only a small effect */ text-indent: calc(var(--current-cycle-svg-size) + var(--task-info-expander-svg-mr)) hanging; } .cycle-icon { @@ -448,7 +451,8 @@ body:not(.light-mode) img.icon-filter { color: var(--text-label); } -#cycle-schedule, #more-info { +#cycle-schedule, +#more-info { table { margin: auto; } @@ -458,7 +462,8 @@ body:not(.light-mode) img.icon-filter { text-decoration: underline var(--menu-panel-border) 1px; text-underline-offset: 0.25em; } - th::before, th::after { + th::before, /* eslint-disable-line css/no-invalid-properties -- eslint bug workaround */ + th::after { content: "\00a0\00a0\00a0\00a0"; /* some no-break spaces added so the underline extends a bit on both sides */ } :is(th, td):not(:last-child) { @@ -467,7 +472,8 @@ body:not(.light-mode) img.icon-filter { td { vertical-align: top; } - th, td:not(:has(.cycle-icon)) { + th, + td { text-align: center; } td:has(.cycle-icon) { @@ -488,16 +494,14 @@ body:not(.light-mode) img.icon-filter { /* More Info */ #more-info { - p:not(:last-child):not(:has(+ :is(ul, table))), table { + p:not(:last-child, :has(+ :is(ul, table))), /* eslint-disable-line css/no-invalid-properties -- eslint bug workaround */ + table { margin-bottom: 1em; } ul { list-style: "\2212 " outside; padding-left: 2em; } - th, td { - text-align: center; - } a { color: var(--link-color); text-decoration: underline; @@ -526,7 +530,7 @@ body:not(.light-mode) img.icon-filter { content: "|"; margin: 0 0.5em; } -.info-line > span > span { +.info-line > span > span { /* stylelint-disable-line no-descending-specificity -- the complained-about selector cannot match the same element as this */ /* keep the info line icon on the same line as the first word of the info line item. * e.g.: {prereq_icon}[NO_BREAK]Mastery[BREAK_OK]Rank[BREAK_OK]3 */ white-space: nowrap; @@ -553,10 +557,10 @@ body:not(.light-mode) img.icon-filter { } @media (prefers-reduced-motion: no-preference) { .task-expander { - transition: grid-template-rows 200ms; + transition: grid-template-rows 0.2s; } .task-expander > .task-item { - transition: margin 200ms; + transition: margin 0.2s; } } @@ -565,28 +569,28 @@ body.hide-completed-tasks .task-expander:has(> .task-item > .checked), body.hide-completed-tasks .task-expander:has(> .parent-task-container > .parent-task-header > .checked) { grid-template-rows: 0fr; @media (prefers-reduced-motion: no-preference) { - transition: grid-template-rows 200ms 5s; + transition: grid-template-rows 0.2s 5s; } @media (prefers-reduced-motion: reduce) { transition: grid-template-rows 0s 5s; } &::after { - background-color: color(from var(--checkbox-checked-bg) srgb r g b / 0.25); + background-color: color(from var(--checkbox-checked-bg) srgb r g b / 25%); width: 0; } } body.hide-completed-tasks .task-expander > .task-item:has(> .checked) { margin: 0; @media (prefers-reduced-motion: no-preference) { - transition: margin 200ms 5s; + transition: margin 0.2s 5s; } @media (prefers-reduced-motion: reduce) { transition: margin 0s 5s; } } /* countdown bar */ -.task-expander { +.task-expander { /* stylelint-disable-line no-descending-specificity -- better to group blocks by function than split them up by specificity */ position: relative; } .task-expander::after { @@ -612,16 +616,14 @@ body.hide-completed-tasks .task-expander > .task-item:has(> .checked) { .list-stats > div { overflow: hidden; display: flex; + margin-bottom: 0.5em; } body.hide-completed-tasks .list-stats:has(> div > button[data-count]:not([data-count="0"])), /* with autohide, show when any stat is nonzero */ body:not(.hide-completed-tasks) .list-stats:has(> div > button:not(.stats-completed)[data-count]:not([data-count="0"])) /* without autohide, show when any stat except completed is nonzero */ { grid-template-rows: 1fr; } @media (prefers-reduced-motion: no-preference) { - .list-stats { transition: grid-template-rows 200ms; } -} -.list-stats > div { - margin-bottom: 0.5em; + .list-stats { transition: grid-template-rows 0.2s; } } .list-stats > div > button[data-count="0"], /* hide stat when equal to 0 */ body:not(.hide-completed-tasks) .list-stats .stats-completed /* hide completed when autohide is off */ { diff --git a/sources/js/app.js b/sources/js/app.js index 67a0e97..59f37b0 100644 --- a/sources/js/app.js +++ b/sources/js/app.js @@ -14,7 +14,7 @@ import { getUTCDayOfYear, formatCountdown, parseDuration, - calcCycleNumber, + calcResetCount, makeInfoLineItem, calcTaskTimes, makeSectionStats, @@ -27,26 +27,26 @@ import * as svgIcons from "./icons.js"; // --- Configuration --- // Array of background div IDs (must match IDs in sources/index.html) const dailyBackgroundImageIds = [ - 'bg-image-0', - 'bg-image-1', - 'bg-image-2', - 'bg-image-3', - 'bg-image-4', + "bg-image-0", + "bg-image-1", + "bg-image-2", + "bg-image-3", + "bg-image-4", // Add more IDs if you add more background image divs in HTML ]; -export const APP_VERSION = "5.3"; +export const APP_VERSION = "5.3.1"; const GIT_COMMIT_HASH_LONG = import.meta.env.VITE_GIT_COMMIT_HASH; -const GIT_COMMIT_HASH = GIT_COMMIT_HASH_LONG.slice(0,7); +const GIT_COMMIT_HASH = GIT_COMMIT_HASH_LONG.slice(0, 7); const WARFRAME_VERSION = "43.0.8"; -const THEME_STORAGE_KEY = 'warframeChecklistTheme'; +const THEME_STORAGE_KEY = "warframeChecklistTheme"; // only update DATA_STORAGE_KEY when the data storage format changes // in a backwards-incompatible way (this should be *very* rare) const DATA_STORAGE_KEY = "warframeChecklistData_format1"; // --- Task Data --- -import tasks from "./tasks.json" with {type: "json"}; -import cycles from "./cycles.json" with {type: "json"}; +import tasks from "./tasks.json" with { type: "json" }; +import cycles from "./cycles.json" with { type: "json" }; import moreInfo from "./moreInfo.js"; // --- DOM Elements (defined after DOMContentLoaded) --- @@ -62,7 +62,7 @@ const confirmState = { all: { timeout: null, isConfirming: false }, daily: { timeout: null, isConfirming: false }, weekly: { timeout: null, isConfirming: false }, - unhide: { timeout: null, isConfirming: false } + unhide: { timeout: null, isConfirming: false }, }; let checklistData = newChecklistData(); @@ -115,7 +115,7 @@ function initializeDOMElements() { * `callback` function with the task definition object as a parameter. */ function forEachTask(callback) { - let stack = []; + const stack = []; for (const section in tasks) { for (let i = 0; i < tasks[section].length; i++) { // top-level tasks are not pushed in reverse order because they're immediately popped @@ -124,9 +124,9 @@ function forEachTask(callback) { const t = stack.pop(); callback(t); // do the thing if (t.subtasks) { - for (let i = t.subtasks.length - 1; i >= 0 ; i--) { + for (let j = t.subtasks.length - 1; j >= 0; j--) { /* eslint-disable-line max-depth */ // subtasks are pushed in reverse order so that they're popped in the correct order - stack.push(t.subtasks[i]); + stack.push(t.subtasks[j]); } } } @@ -149,9 +149,7 @@ function prepTasks() { task.section = task.id.split("_")[0]; if (["daily", "weekly"].includes(task.section)) { - if (task.ref) { // alternate ref tasks need a period for countdown and reset to work correctly - task.period = (task.section === "daily") ? "1d" : "7d"; - } + task.period = (task.section === "daily") ? "1d" : "7d"; } if (task.id in moreInfo) { @@ -178,7 +176,7 @@ function displayError(message) { function hideError() { if (!errorDisplayElement) { return; } errorDisplayElement.classList.remove("visible"); - errorMessageElement.textContent = ''; + errorMessageElement.textContent = ""; if (errorCopyButton) { errorCopyButton.textContent = "Copy"; } } @@ -210,16 +208,16 @@ function copyErrorToClipboard() { function applyTheme(theme) { if (!bodyElement || !themeToggleButton) { return; } - if (theme === 'light') { - bodyElement.classList.add('light-mode'); + if (theme === "light") { + bodyElement.classList.add("light-mode"); } else { - bodyElement.classList.remove('light-mode'); + bodyElement.classList.remove("light-mode"); } currentTheme = theme; } function handleThemeToggle() { - const newTheme = currentTheme === 'light' ? 'dark' : 'light'; + const newTheme = currentTheme === "light" ? "dark" : "light"; applyTheme(newTheme); try { localStorage.setItem(THEME_STORAGE_KEY, newTheme); @@ -235,10 +233,10 @@ function loadThemePreference() { return; } - if (bodyElement.classList.contains('light-mode')) { - currentTheme = 'light'; + if (bodyElement.classList.contains("light-mode")) { + currentTheme = "light"; } else { - currentTheme = 'dark'; + currentTheme = "dark"; } try { const storedTheme = localStorage.getItem(THEME_STORAGE_KEY); @@ -261,8 +259,8 @@ function updateLastSavedDisplay(timestamp) { function showSaveStatus() { if (!saveStatusElement) { return; } clearTimeout(saveStatusTimeout); - saveStatusElement.style.opacity = '1'; - saveStatusTimeout = setTimeout(() => { saveStatusElement.style.opacity = '0'; }, 1500); + saveStatusElement.style.opacity = "1"; + saveStatusTimeout = setTimeout(() => { saveStatusElement.style.opacity = "0"; }, 1500); } function setDailyBackground() { @@ -275,14 +273,14 @@ function setDailyBackground() { const dayOfYear = getUTCDayOfYear(now); const imageIndex = dayOfYear % backgroundDivs.length; - console.log(`Setting daily background to show div: ${backgroundDivs[imageIndex] ? backgroundDivs[imageIndex].id : 'N/A'} (Index: ${imageIndex})`); + console.log(`Setting daily background to show div: ${backgroundDivs[imageIndex] ? backgroundDivs[imageIndex].id : "N/A"} (Index: ${imageIndex})`); backgroundDivs.forEach((div, index) => { if (div) { // Ensure div exists if (index === imageIndex) { - div.style.display = 'block'; + div.style.display = "block"; } else { - div.style.display = 'none'; + div.style.display = "none"; } } }); @@ -320,8 +318,8 @@ function displayLocalResetTimes() { tasks.other.forEach((task) => displayOtherTaskCountdown(task)); } catch (e) { console.error("Error calculating or displaying local reset times:", e); - if (dailyResetTimeElement) { dailyResetTimeElement.innerHTML = `(Resets 00:00 UTC)`; } - if (weeklyResetTimeElement) { weeklyResetTimeElement.innerHTML = `(Resets Mon 00:00 UTC)`; } + if (dailyResetTimeElement) { dailyResetTimeElement.innerHTML = "(Resets 00:00 UTC)"; } + if (weeklyResetTimeElement) { weeklyResetTimeElement.innerHTML = "(Resets Mon 00:00 UTC)"; } } } @@ -331,6 +329,7 @@ export function displayOtherTaskCountdown(task) { const now = new Date(); const taskTimes = calcTaskTimes(task, now); + const resetCount = calcResetCount(task, now); if (task.duration) { // intermittently available task (e.g., Baro) const leaveNotifId = `${task.id}/departure`; @@ -339,28 +338,60 @@ export function displayOtherTaskCountdown(task) { const diff = taskTimes.thisCycleLeaveTimestamp - now.getTime(); resetTimer.innerHTML = `(Available for ${formatCountdown(diff)})`; - // Leaving soon notification (Arrival notification is handled in runAutoResets, the same as always available tasks) - if (diff < C.MILLISECONDS_PER_HOUR && checklistData.notificationPreferences[task.id] && checklistData.notificationsSent[leaveNotifId] !== cycleNumber) { + // Leaving soon notification (Arrival notification is handled in otherTaskReset, the same as always available tasks) + if (diff < C.MILLISECONDS_PER_HOUR && checklistData.notificationPreferences[task.id] && checklistData.notificationsSent[leaveNotifId] !== resetCount) { showNotification(`${task.title} Leaving Soon!`, `Approximately ${Math.round(diff / C.MILLISECONDS_PER_MINUTE)} minutes remaining.`); - checklistData.notificationsSent[leaveNotifId] = cycleNumber; + checklistData.notificationsSent[leaveNotifId] = resetCount; saveData(false); } } else { // task not available resetTimer.innerHTML = `(Available in ${formatCountdown(taskTimes.nextResetTimestamp - now.getTime())})`; - if (checklistData.notificationsSent[leaveNotifId]) {delete checklistData.notificationsSent[leaveNotifId];} + if (checklistData.notificationsSent[leaveNotifId]) { delete checklistData.notificationsSent[leaveNotifId]; } } } else { // always available task resetTimer.innerHTML = `(Resets in ${formatCountdown(taskTimes.nextResetTimestamp - now.getTime())})`; } } +/** + * Time travel (adjusting the system clock) may be used for debugging purposes, which leaves behind some annoying + * artifacts in the save data. This cleans them up. + */ +function fixTimeTravel() { + const now = new Date(); + + for (const prop of ["lastSaved", "lastDailyReset", "lastWeeklyReset"]) { + if (new Date(checklistData[prop]) > now) { + checklistData[prop] = null; + console.warn(`fixed time travel of ${prop}`); + } + } + + for (const taskId in checklistData.lastTaskResetTimes) { + if (checklistData.lastTaskResetTimes[taskId] > now.getTime()) { + checklistData.lastTaskResetTimes[taskId] = 0; + console.warn(`fixed time travel of ${taskId} reset`); + } + } + + for (const notifId in checklistData.notificationsSent) { + const taskId = notifId.split("/")[0]; + const resetCount = calcResetCount(getTaskById(taskId), now); + if (checklistData.notificationsSent[notifId] > resetCount) { + delete checklistData.notificationsSent[notifId]; + console.warn(`fixed time travel of ${notifId} notification`); + } + } +} + function runAutoResets() { + fixTimeTravel(); const now = new Date(); const nowUTCTimestamp = now.getTime(); const todayUTCString = getUTCDateString(now); const lastDailyResetDate = checklistData.lastDailyReset ? new Date(checklistData.lastDailyReset) : null; - let lastDailyResetUTCString = lastDailyResetDate ? getUTCDateString(lastDailyResetDate) : null; + const lastDailyResetUTCString = lastDailyResetDate ? getUTCDateString(lastDailyResetDate) : null; if (!lastDailyResetUTCString || lastDailyResetUTCString !== todayUTCString) { console.log(`Performing daily auto-reset (UTC). Today: ${todayUTCString}, Last Reset: ${lastDailyResetUTCString}`); resetSection("daily", false); @@ -388,32 +419,27 @@ function runAutoResets() { } function otherTaskReset(task) { - if (["daily", "weekly"].includes(task.section) && !task.ref) { // daily and weekly tasks with an alternate ref are handled as "other" tasks + if (["daily", "weekly"].includes(task.section) && !task.ref) { // daily and weekly tasks without an alternate ref are not handled as "other" tasks return false; - } else if (!task.period) { - console.error(`[${task.id}] other_* tasks MUST specify a "period"`); - return false; - } - if (!task.ref) { - console.warn(`[${task.id}] other_* tasks SHOULD specify a "ref". The default ref of 0 ("1970-01-01T00:00:00Z") will be used otherwise.`) } let didReset = false; const now = new Date(); - const cycleNumber = calcCycleNumber(task, now); + const resetCount = calcResetCount(task, now); const lastResetTime = checklistData.lastTaskResetTimes[task.id] || 0; - const lastResetCycleNumber = calcCycleNumber(task, lastResetTime); + const lastResetCount = calcResetCount(task, lastResetTime); - if (checklistData.progress[task.id] && !calcTaskTimes(task, now).isAvailable) { + if (!calcTaskTimes(task, now).isAvailable && (checklistData.progress[task.id] || !document.getElementById(task.id)?.indeterminate)) { // Uncheck unavailable task checklistData.progress[task.id] = false; + console.log(`${task.id} is now unavailalbe`); didReset = true; } - if (cycleNumber > lastResetCycleNumber) { + if (resetCount > lastResetCount) { // Reset task - if (checklistData.progress[task.id] || checklistData.skippedTasks[task.id]) { + if (checklistData.progress[task.id] || checklistData.skippedTasks[task.id] || task.duration) { checklistData.progress[task.id] = false; checklistData.skippedTasks[task.id] = false; console.log(`Resetting task: ${task.id}`); @@ -423,14 +449,14 @@ function otherTaskReset(task) { // Delete old notification record const notifSent = checklistData.notificationsSent[task.id]; - if (notifSent && notifSent !== cycleNumber) { + if (notifSent && notifSent !== resetCount) { delete checklistData.notificationsSent[task.id]; } // Send and record new notification - if (checklistData.notificationPreferences[task.id] && checklistData.notificationsSent[task.id] !== cycleNumber) { + if (checklistData.notificationPreferences[task.id] && checklistData.notificationsSent[task.id] !== resetCount) { showNotification(`${task.title} has reset!`, "Vendor stock may have updated."); - checklistData.notificationsSent[task.id] = cycleNumber; + checklistData.notificationsSent[task.id] = resetCount; saveData(false); } } @@ -467,7 +493,7 @@ function showNotification(title, body) { } } -function createChecklistItem(task) { +function createChecklistItem(task) { /* eslint-disable-line complexity, max-lines-per-function */ const isChecked = checklistData.progress[task.id] || false; const isAvailable = calcTaskTimes(task, new Date()).isAvailable; @@ -524,7 +550,7 @@ function createChecklistItem(task) { } // Reset Timer - if (task.period) { + if (task.ref) { const resetTimer = document.createElement("span"); resetTimer.classList.add("other-countdown"); resetTimer.textContent = "(Loading...)"; @@ -566,7 +592,7 @@ function createChecklistItem(task) { let skipTooltip; if (isAvailable) { let when = "this cycle"; - if (task.id.startsWith("daily_")) { when = "today"; } + if (task.id.startsWith("daily_")) { when = "today"; } /* eslint-disable-line @stylistic/brace-style */ else if (task.id.startsWith("weekly_")) { when = "this week"; } skipTooltip = `Skip task ${when}: ${task.title}`; } else { @@ -623,14 +649,14 @@ function createChecklistItem(task) { subtaskList.appendChild(makeSectionStats(task.id)); calcSectionStats(task.id, subtaskList); - subtaskCollapsible.appendChild(subtaskList) + subtaskCollapsible.appendChild(subtaskList); taskItem.appendChild(subtaskCollapsible); updateIncompleteSubtaskCount(task, taskItem); // On Click -> Collapse/Expand parentHeaderDiv.addEventListener("click", (e) => { - if (e.target !== checkbox && !checkbox.contains(e.target) && !controlsContainer.contains(e.target) && !collapseButton.contains(e.target) ) { + if (e.target !== checkbox && !checkbox.contains(e.target) && !controlsContainer.contains(e.target) && !collapseButton.contains(e.target)) { toggleCollapseSubtasks(task); } }); @@ -641,8 +667,7 @@ function createChecklistItem(task) { // Checkbox Changed -> Change Subtasks Checkboxes checkbox.addEventListener("change", checkboxChangeAction(task)); - - } else { //no subtasks + } else { // no subtasks taskItem.appendChild(checkbox); if (task.icon) { taskItem.appendChild(icon); } taskItem.appendChild(taskDescription); @@ -678,7 +703,7 @@ function checkboxChangeAction(task) { // don't know about the checkbox status of the originating event. So we attach that status to the // `detail` of a CustomEvent, which takes priority over the local status. (Recursive firings like this // allow for nested subtasks of arbitrary depth) - subCheckbox.dispatchEvent(new CustomEvent("change", {detail: currentlyChecked})); + subCheckbox.dispatchEvent(new CustomEvent("change", { detail: currentlyChecked })); }); updateIncompleteSubtaskCount(task); } else { @@ -688,28 +713,28 @@ function checkboxChangeAction(task) { calcSectionStats(task.section); saveData(); - } + }; } function hideOrSkipTaskAction(task, skip) { return (event) => { event.stopPropagation(); - if (skip) { checklistData.skippedTasks[task.id] = true; } + if (skip) { checklistData.skippedTasks[task.id] = true; } /* eslint-disable-line @stylistic/brace-style */ else { checklistData.hiddenTasks[task.id] = true; } const taskItem = event.target.closest(".task-item"); taskItem.classList.add("hidden-task"); updateParentCheckboxes(task); updateSectionControls(taskItem.closest("section").id); saveData(false); - } + }; } function updateParentCheckboxes(task) { let t = task; - while (t.parentId) { // walk up the task tree + while (t.parentId) { // walk up the task tree calcSectionStats(t.parentId); - let parentTaskDefinition = getTaskById(t.parentId); + const parentTaskDefinition = getTaskById(t.parentId); if (parentTaskDefinition && parentTaskDefinition.subtasks) { const allSubtasksDone = parentTaskDefinition.subtasks.every((st) => (checklistData.progress[st.id] || checklistData.hiddenTasks[st.id] || checklistData.skippedTasks[st.id])); @@ -723,7 +748,7 @@ function updateParentCheckboxes(task) { if (parentDescription) { parentDescription.classList.toggle("checked", allSubtasksDone); } } - t = parentTaskDefinition; // move up a level + t = parentTaskDefinition; // move up a level } calcSectionStats(task.section); saveData(); @@ -739,10 +764,10 @@ function toggleCollapseSubtasks(task) { updateIncompleteSubtaskCount(task); } -function updateIncompleteSubtaskCount(task, queryFrom=document) { +function updateIncompleteSubtaskCount(task, queryFrom = document) { if (task.subtasks) { const element = queryFrom.querySelector(`#${task.id} ~ .collapse-btn .incomplete-count > div`); - const unchecked = queryFrom.querySelectorAll(`input[data-parent-id="${task.id}"]:not(:checked, :indeterminate, .hidden-task *)`) + const unchecked = queryFrom.querySelectorAll(`input[data-parent-id="${task.id}"]:not(:checked, :indeterminate, .hidden-task *)`); element.innerText = unchecked.length; element.parentElement.dataset.count = unchecked.length; element.parentElement.title = `${unchecked.length} incomplete subtasks of ${task.title}`; @@ -752,7 +777,7 @@ function updateIncompleteSubtaskCount(task, queryFrom=document) { function taskDialogHeaderSetup(task, dialog) { dialog.querySelector(":scope header .task-title").innerHTML = task?.title || ""; - let taskIcon = dialog.querySelector(":scope .menu-title img.task-icon"); + const taskIcon = dialog.querySelector(":scope .menu-title img.task-icon"); taskIcon.className = "task-icon"; // remove possible `icon-filter` from previous opening taskIcon.src = ""; if (task?.icon) { @@ -763,9 +788,7 @@ function taskDialogHeaderSetup(task, dialog) { } } -function showScheduleAction(task, period, cycleIndex, isAvailable) { - const cycleCount = cycles[task.id].columns[0].order.length; - +function showScheduleAction(task, cycleIndex, isAvailable) { return () => { taskDialogHeaderSetup(task, scheduleDialog); @@ -774,13 +797,16 @@ function showScheduleAction(task, period, cycleIndex, isAvailable) { thead.innerHTML = ""; tbody.innerHTML = ""; - let header = `Date`; + let header = "Date"; for (const column of cycles[task.id].columns) { header += `${column.name}`; } header += ""; thead.innerHTML += header; + const cycleCount = cycles[task.id].columns[0].order.length; + const period = parseDuration(task.period); + const now = new Date(); const ref = new Date(cycles[task.id].ref); const cyclesSinceRef = Math.floor((now.getTime() - ref.getTime()) / period) + (isAvailable ? 0 : 1); // add 1 if unavailable to skip the current cycle for unavailable intermittent tasks @@ -810,7 +836,7 @@ function showScheduleAction(task, period, cycleIndex, isAvailable) { tbody.innerHTML += row; } scheduleDialog.showModal(); - } + }; } function showMoreInfoAction(task) { @@ -818,10 +844,10 @@ function showMoreInfoAction(task) { taskDialogHeaderSetup(task, moreInfoDialog); document.getElementById("more-info-content").innerHTML = task.moreInfo; moreInfoDialog.showModal(); - } + }; } -function makeInfoLine(task, appendTo) { +function makeInfoLine(task, appendTo) { /* eslint-disable-line max-lines-per-function */ const hasCycle = Object.hasOwn(cycles, task.id); const hasInfoLine = ["location", "npc", "terminal", "prereq", "info", "moreInfo"].some((prop) => task[prop]); @@ -841,26 +867,19 @@ function makeInfoLine(task, appendTo) { const ref = new Date(cycles[task.id].ref); const cycleCount = cycles[task.id].columns[0].order.length; - let prefix, period, cycleIndex; + let prefix; const isAvailable = calcTaskTimes(task, now).isAvailable; - if (task.id.startsWith("weekly_")) { + if (task.section === "weekly") { prefix = "This Week"; - period = 7 * C.MILLISECONDS_PER_DAY; - if (ref.getUTCDay() !== 1) { - console.warn(`${task.id} cycle ref ${cycles[task.id].ref} is not a Monday`); - } - } - else if (task.id.startsWith("daily_")) { + } else if (task.section === "daily") { prefix = "Today"; - period = C.MILLISECONDS_PER_DAY; - } - else { + } else { prefix = "Current Cycle"; - if (!isAvailable) {prefix = "Next Cycle";} - period = parseDuration(task.period); + if (!isAvailable) { prefix = "Next Cycle"; } } - cycleIndex = modulo(Math.floor((now.getTime() - ref.getTime()) / period), cycleCount); + const resetCount = calcResetCount(task, now, ref); // calcResetCount handles DST + let cycleIndex = modulo(resetCount, cycleCount); if (!isAvailable) { cycleIndex = modulo(cycleIndex + 1, cycleCount); } console.log(`${task.id} cycleIndex ${cycleIndex}`); const cycleData = cycles[task.id].columns[0].order[cycleIndex]; @@ -879,7 +898,7 @@ function makeInfoLine(task, appendTo) { showSchedule.type = "button"; showSchedule.classList.add("more-info-btn"); showSchedule.innerHTML = "Show Schedule"; - showSchedule.addEventListener("click", showScheduleAction(task, period, cycleIndex, isAvailable)); + showSchedule.addEventListener("click", showScheduleAction(task, cycleIndex, isAvailable)); currentCycle.appendChild(showSchedule); taskInfoExpanderContent.appendChild(currentCycle); @@ -887,7 +906,7 @@ function makeInfoLine(task, appendTo) { // Info Line if (hasInfoLine) { - if (task.npc && task.terminal) {console.warn(`[${task.id}] Tasks should specify only one of [npc, terminal].`);} + if (task.npc && task.terminal) { console.warn(`[${task.id}] Tasks should specify only one of [npc, terminal].`); } const infoLine = document.createElement("div"); infoLine.classList.add("info-line"); let infoLineHTML = ""; @@ -932,7 +951,7 @@ function populateSection(section) { sectionElement.appendChild(makeSectionStats(section)); calcSectionStats(section); - let updatedParents = new Set(); + const updatedParents = new Set(); forEachTask((task) => { if (task.section === section && task.parentId && !task.subtasks && !updatedParents.has(task.parentId)) { // if the task is a non-root leaf node in the current section, and the first among its siblings @@ -950,7 +969,7 @@ function populateSection(section) { * @param parent - what task list to count stats on. This can be the name of a section, or the id of a task with subtasks. * @param queryFrom - DOM element to find the stats box in. Defaults to `document`. Override this if the element is not inserted into the document yet. */ -function calcSectionStats(parent, queryFrom=document) { +function calcSectionStats(parent, queryFrom = document) { let taskList; if (parent.includes("_")) { taskList = getTaskById(parent).subtasks; @@ -959,15 +978,15 @@ function calcSectionStats(parent, queryFrom=document) { } const statsBox = queryFrom.querySelector(`#stats_${parent} > div`); - let stats = { + const stats = { completed: 0, skipped: 0, - hidden: 0 - } + hidden: 0, + }; for (const task of taskList) { - if (checklistData.progress[task.id]) { stats.completed++; } + if (checklistData.progress[task.id]) { stats.completed++; } if (checklistData.skippedTasks[task.id]) { stats.skipped++; } - if (checklistData.hiddenTasks[task.id]) { stats.hidden++; } + if (checklistData.hiddenTasks[task.id]) { stats.hidden++; } } statsBox.innerHTML = ""; @@ -975,8 +994,8 @@ function calcSectionStats(parent, queryFrom=document) { const statIcons = { completed: "✔", skipped: svgIcons.skipIcon, - hidden: svgIcons.hideIcon - } + hidden: svgIcons.hideIcon, + }; for (const stat in stats) { const statButton = document.createElement("button"); @@ -1054,7 +1073,7 @@ function showHiddenTasksAction(parent, taskList, stat) { } hiddenTasksDialog.showModal(); - } + }; } function hiddenTasksButtonAction(taskElement, task, stat) { @@ -1078,7 +1097,7 @@ function hiddenTasksButtonAction(taskElement, task, stat) { if (!hiddenTasksDialog.querySelector("li > :not(.hidden-task)")) { hiddenTasksDialog.close(); } - } + }; } function resetSpecificButtonState(buttonElement, defaultText, stateKey) { @@ -1087,7 +1106,7 @@ function resetSpecificButtonState(buttonElement, defaultText, stateKey) { confirmState[stateKey].timeout = null; confirmState[stateKey].isConfirming = false; buttonElement.textContent = defaultText; - buttonElement.classList.remove('confirming'); + buttonElement.classList.remove("confirming"); console.log(`Button ${buttonElement.id} state reverted.`); } @@ -1101,10 +1120,12 @@ function handleResetConfirmation(buttonElement, confirmKey, defaultText, resetAc Object.keys(confirmState).forEach((key) => { if (key !== confirmKey && confirmState[key].isConfirming) { let btnElement, btnText; - if (key === 'all') { btnElement = resetButton; btnText = 'Reset All Checks'; } - else if (key === 'daily') { btnElement = resetDailyButton; btnText = 'Reset Daily Checks'; } - else if (key === 'weekly') { btnElement = resetWeeklyButton; btnText = 'Reset Weekly Checks'; } - else if (key === 'unhide') { btnElement = unhideTasksButton; btnText = 'Unhide All Tasks'; } + /* eslint-disable @stylistic/max-statements-per-line, @stylistic/brace-style */ + if (key === "all") { btnElement = resetButton; btnText = "Reset All Checks"; } + else if (key === "daily") { btnElement = resetDailyButton; btnText = "Reset Daily Checks"; } + else if (key === "weekly") { btnElement = resetWeeklyButton; btnText = "Reset Weekly Checks"; } + else if (key === "unhide") { btnElement = unhideTasksButton; btnText = "Unhide All Tasks"; } + /* eslint-enable @stylistic/max-statements-per-line, @stylistic/brace-style */ if (btnElement) { resetSpecificButtonState(btnElement, btnText, key); } @@ -1118,8 +1139,8 @@ function handleResetConfirmation(buttonElement, confirmKey, defaultText, resetAc resetSpecificButtonState(buttonElement, defaultText, confirmKey); } else { confirmState[confirmKey].isConfirming = true; - buttonElement.textContent = 'Are you Sure?'; - buttonElement.classList.add('confirming'); + buttonElement.textContent = "Are you Sure?"; + buttonElement.classList.add("confirming"); clearTimeout(confirmState[confirmKey].timeout); confirmState[confirmKey].timeout = setTimeout(() => { @@ -1138,7 +1159,7 @@ function resetAllAction() { console.log("Checklist reset complete."); } -function resetSection(section, resetAltRefTasks=false) { +function resetSection(section, resetAltRefTasks = false) { const validSections = ["daily", "weekly"]; if (!validSections.includes(section)) { console.error("Section '%s' is not a valid section name: %s", section, validSections); @@ -1160,7 +1181,7 @@ function resetSection(section, resetAltRefTasks=false) { tasks[section].forEach(resetTask); const now = new Date().toISOString(); - if (section === "daily") { checklistData.lastDailyReset = now; } + if (section === "daily") { checklistData.lastDailyReset = now; } /* eslint-disable-line @stylistic/brace-style */ else if (section === "weekly") { checklistData.lastWeeklyReset = now; } saveData(); if (didReset) { @@ -1178,17 +1199,17 @@ function resetWeeklyAction() { } function handleSectionToggle(event) { - const header = event.target.closest('.section-toggle'); + const header = event.target.closest(".section-toggle"); if (!header) { return; } - const contentId = header.getAttribute('aria-controls'); + const contentId = header.getAttribute("aria-controls"); const contentDiv = document.getElementById(contentId); if (!contentDiv) { return; } - const isExpanded = header.getAttribute('aria-expanded') === 'true'; - header.setAttribute('aria-expanded', !isExpanded); - contentDiv.classList.toggle('collapsed', isExpanded); - console.log(`Toggled section ${contentId} to ${!isExpanded ? 'expanded' : 'collapsed'}`); + const isExpanded = header.getAttribute("aria-expanded") === "true"; + header.setAttribute("aria-expanded", !isExpanded); + contentDiv.classList.toggle("collapsed", isExpanded); + console.log(`Toggled section ${contentId} to ${isExpanded ? "collapsed" : "expanded"}`); } function unhideAllAction() { @@ -1208,35 +1229,35 @@ function updateSectionControls(sectionElementId) { const sectionElement = document.getElementById(sectionElementId); if (!sectionElement) { return; } - const hideSectionButton = sectionElement.querySelector('.hide-section-button'); + const hideSectionButton = sectionElement.querySelector(".hide-section-button"); if (!hideSectionButton) { return; } if (checklistData.manuallyHiddenSections[sectionElementId]) { - sectionElement.classList.add('section-is-hidden-by-user'); - hideSectionButton.classList.remove('visible'); + sectionElement.classList.add("section-is-hidden-by-user"); + hideSectionButton.classList.remove("visible"); return; } else { - sectionElement.classList.remove('section-is-hidden-by-user'); + sectionElement.classList.remove("section-is-hidden-by-user"); } - const contentUl = sectionElement.querySelector('.section-content ul'); + const contentUl = sectionElement.querySelector(".section-content ul"); if (!contentUl) { - hideSectionButton.classList.remove('visible'); + hideSectionButton.classList.remove("visible"); return; } - const allTaskItems = Array.from(contentUl.querySelectorAll('li.task-item')); + const allTaskItems = Array.from(contentUl.querySelectorAll("li.task-item")); if (allTaskItems.length === 0) { - hideSectionButton.classList.remove('visible'); + hideSectionButton.classList.remove("visible"); return; } const allTasksIndividuallyHidden = allTaskItems.every((item) => item.classList.contains("hidden-task")); if (allTasksIndividuallyHidden) { - hideSectionButton.classList.add('visible'); + hideSectionButton.classList.add("visible"); } else { - hideSectionButton.classList.remove('visible'); + hideSectionButton.classList.remove("visible"); } } @@ -1259,7 +1280,7 @@ function saveData(showStatus = true) { } catch (e) { console.error("Error saving data to localStorage:", e); let userMessage = "Could not save progress."; - if (e.name === 'QuotaExceededError' || (e.code && (e.code === 22 || e.code === 1014))) { + if (e.name === "QuotaExceededError" || (e.code && (e.code === 22 || e.code === 1014))) { userMessage = "Could not save progress. Browser storage might be full."; } displayError(userMessage); @@ -1273,7 +1294,7 @@ function loadData() { const parsedData = JSON.parse(savedData); if (parsedData && typeof parsedData === "object") { for (const key of Object.keys(checklistData)) { - if (key in parsedData) { + if (key in parsedData) { /* eslint-disable-line max-depth */ checklistData[key] = parsedData[key]; } } @@ -1286,7 +1307,7 @@ function loadData() { } } -export function loadAndInitializeApp() { +export function loadAndInitializeApp() { /* eslint-disable-line max-lines-per-function */ initializeDOMElements(); hideError(); loadThemePreference(); @@ -1299,7 +1320,7 @@ export function loadAndInitializeApp() { updateLastSavedDisplay(checklistData.lastSaved); // Setup event listeners - if (appVersionElement) { appVersionElement.textContent = APP_VERSION; } + if (appVersionElement) { appVersionElement.textContent = APP_VERSION; } /* eslint-disable-line @stylistic/brace-style */ else { console.error("App version element not found!"); } if (gitHashElement) { @@ -1307,8 +1328,9 @@ export function loadAndInitializeApp() { gitHashElement.href = `https://github.com/warframe-tools/Task-Checklist/tree/${GIT_COMMIT_HASH_LONG}`; } else { console.error("git hash element not found!"); } - if (wfVersionElement) { wfVersionElement.textContent = `Warframe Version ${WARFRAME_VERSION}`; } - else { console.error("Warframe version element not found!"); } + if (wfVersionElement) { + wfVersionElement.textContent = `Warframe Version ${WARFRAME_VERSION}`; + } else { console.error("Warframe version element not found!"); } if (hideCompletedToggle) { hideCompletedToggle.addEventListener("change", (event) => { @@ -1318,59 +1340,64 @@ export function loadAndInitializeApp() { }); hideCompletedToggle.checked = checklistData.hideCompletedTasks; bodyElement.classList.toggle("hide-completed-tasks", checklistData.hideCompletedTasks); - } - else { console.error("Hide Completed Toggle not found!"); } + } else { console.error("Hide Completed Toggle not found!"); } - if (resetDailyButton) { resetDailyButton.addEventListener('click', () => handleResetConfirmation(resetDailyButton, 'daily', 'Reset Daily Checks', resetDailyAction)); } - else { console.error("Reset Daily button element not found!"); } + if (resetDailyButton) { + resetDailyButton.addEventListener("click", () => handleResetConfirmation(resetDailyButton, "daily", "Reset Daily Checks", resetDailyAction)); + } else { console.error("Reset Daily button element not found!"); } - if (resetWeeklyButton) { resetWeeklyButton.addEventListener('click', () => handleResetConfirmation(resetWeeklyButton, 'weekly', 'Reset Weekly Checks', resetWeeklyAction)); } - else { console.error("Reset Weekly button element not found!"); } + if (resetWeeklyButton) { + resetWeeklyButton.addEventListener("click", () => handleResetConfirmation(resetWeeklyButton, "weekly", "Reset Weekly Checks", resetWeeklyAction)); + } else { console.error("Reset Weekly button element not found!"); } - if (resetButton) { resetButton.addEventListener('click', () => handleResetConfirmation(resetButton, 'all', 'Reset All Checks', resetAllAction)); } - else { console.error("Reset All button element not found!"); } + if (resetButton) { + resetButton.addEventListener("click", () => handleResetConfirmation(resetButton, "all", "Reset All Checks", resetAllAction)); + } else { console.error("Reset All button element not found!"); } - if (unhideTasksButton) { unhideTasksButton.addEventListener('click', () => handleResetConfirmation(unhideTasksButton, 'unhide', 'Unhide All Tasks', unhideAllAction)); } - else { console.error("Unhide Tasks button not found!");} + if (unhideTasksButton) { + unhideTasksButton.addEventListener("click", () => handleResetConfirmation(unhideTasksButton, "unhide", "Unhide All Tasks", unhideAllAction)); + } else { console.error("Unhide Tasks button not found!"); } - if (themeToggleButton) { themeToggleButton.addEventListener('click', handleThemeToggle); } - else { console.error("Theme toggle button not found!"); } + if (themeToggleButton) { + themeToggleButton.addEventListener("click", handleThemeToggle); + } else { console.error("Theme toggle button not found!"); } - if (hamburgerButton) { hamburgerButton.addEventListener('click', () => {optionsMenu.showModal();}); } - else { console.error("Hamburger button not found!"); } + if (hamburgerButton) { + hamburgerButton.addEventListener("click", () => { optionsMenu.showModal(); }); + } else { console.error("Hamburger button not found!"); } sectionToggles.forEach((toggle) => { toggle.addEventListener("click", handleSectionToggle); }); try { - document.getElementById('checklist-container').addEventListener('click', function(event) { - if (event.target.classList.contains('hide-section-button')) { + document.getElementById("checklist-container").addEventListener("click", (event) => { + if (event.target.classList.contains("hide-section-button")) { const sectionId = event.target.dataset.sectionId; if (sectionId) { const sectionElement = document.getElementById(sectionId); if (sectionElement) { checklistData.manuallyHiddenSections[sectionId] = true; - sectionElement.classList.add('section-is-hidden-by-user'); - event.target.classList.remove('visible'); + sectionElement.classList.add("section-is-hidden-by-user"); + event.target.classList.remove("visible"); saveData(false); console.log(`Section ${sectionId} manually hidden.`); } } } }); - } catch (e) { + } catch { console.error("Checklist container not found!"); } if (errorCloseButton) { - errorCloseButton.addEventListener('click', hideError); + errorCloseButton.addEventListener("click", hideError); } else { console.error("Error close button not found!"); } if (errorCopyButton) { - errorCopyButton.addEventListener('click', copyErrorToClipboard); + errorCopyButton.addEventListener("click", copyErrorToClipboard); } else { console.error("Error copy button not found!"); } for (const dialog of document.getElementsByTagName("dialog")) { @@ -1398,7 +1425,7 @@ document.addEventListener("DOMContentLoaded", () => { try { loadAndInitializeApp(); startCountdown(); - } catch(error) { + } catch (error) { console.error("Critical Error during app.js initialization:", error); const errDisp = document.getElementById("error-display"); const errMsg = document.getElementById("error-message"); diff --git a/sources/js/constants.js b/sources/js/constants.js index 2c8253b..a2f4d11 100644 --- a/sources/js/constants.js +++ b/sources/js/constants.js @@ -4,7 +4,7 @@ export const MILLISECONDS_PER_MINUTE = 60 * MILLISECONDS_PER_SECOND; export const MILLISECONDS_PER_HOUR = 60 * MILLISECONDS_PER_MINUTE; export const MILLISECONDS_PER_DAY = 24 * MILLISECONDS_PER_HOUR; -export const SERVER_TIMEZONE = "America/Toronto"; // used for determining Daylight Saving Time +export const SERVER_TIMEZONE = "America/Toronto"; // used for determining Daylight Saving Time export const BASE_OF_OPERATIONS_TOOLTIP = 'Base of Operations'; @@ -17,7 +17,7 @@ export const TASKLIST_STAT_NAMES = { export const TASKLIST_STAT_PROPERTIES = { "completed": "progress", "skipped": "skippedTasks", - "hidden": "hiddenTasks" + "hidden": "hiddenTasks", }; export const SECTION_NAMES = { diff --git a/sources/js/functions.js b/sources/js/functions.js index 61e510d..9da275f 100644 --- a/sources/js/functions.js +++ b/sources/js/functions.js @@ -28,7 +28,7 @@ export function newChecklistData() { }; } -const taskIcons = import.meta.glob("../img/icons/**/*.png", {eager: true, query: '?url', import: 'default'}); +const taskIcons = import.meta.glob("../img/icons/**/*.png", { eager: true, query: "?url", import: "default" }); export function iconURL(iconName) { return taskIcons["../img/icons/" + iconName]; } @@ -51,7 +51,7 @@ export function makeTaskIcon(task) { export function makeCycleIcon(cycleData) { if (cycleData.icon) { - const src = iconURL(`cycles/${cycleData.icon}`) + const src = iconURL(`cycles/${cycleData.icon}`); let classList = "cycle-icon"; if (cycleData.iconFilter) { classList += " icon-filter"; @@ -63,12 +63,15 @@ export function makeCycleIcon(cycleData) { } export function formatTimestamp(timestamp) { - if (!timestamp) return 'Never'; + if (!timestamp) { return "Never"; } try { const date = new Date(timestamp); - if (isNaN(date.getTime())) return 'Invalid Date'; - return date.toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' }); - } catch (e) { console.error("Error formatting timestamp:", e); return 'Error'; } + if (isNaN(date.getTime())) { return "Invalid Date"; } + return date.toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" }); + } catch (e) { + console.error("Error formatting timestamp:", e); + return "Error"; + } } export function getMostRecentMondayMidnightUTC() { @@ -91,8 +94,8 @@ export function getNextDailyMidnightUTC() { export function getUTCDateString(dateObj) { const year = dateObj.getUTCFullYear(); - const month = (dateObj.getUTCMonth() + 1).toString().padStart(2, '0'); - const day = dateObj.getUTCDate().toString().padStart(2, '0'); + const month = (dateObj.getUTCMonth() + 1).toString().padStart(2, "0"); + const day = dateObj.getUTCDate().toString().padStart(2, "0"); return `${year}-${month}-${day}`; } @@ -113,7 +116,7 @@ export function formatCountdown(ms) { ms %= C.MILLISECONDS_PER_MINUTE; const seconds = Math.floor(ms / C.MILLISECONDS_PER_SECOND); - const pad = (num) => String(num).padStart(2, '0'); + function pad(num) { return String(num).padStart(2, "0"); } if (days > 0) { return `${days}d ${pad(hours)}:${pad(minutes)}:${pad(seconds)}`; @@ -127,16 +130,16 @@ export function formatCountdown(ms) { * Case insensitive. Integers only. */ export function parseDuration(str) { - if (!str || typeof str !== "string") {return undefined;} + if (!str || typeof str !== "string") { return undefined; } const map = { "d": C.MILLISECONDS_PER_DAY, "h": C.MILLISECONDS_PER_HOUR, "m": C.MILLISECONDS_PER_MINUTE, - "s": C.MILLISECONDS_PER_SECOND + "s": C.MILLISECONDS_PER_SECOND, }; return str.toLowerCase().matchAll(/((\d+)([a-z]))/g).toArray().reduce((acc, curr) => { - const n = parseInt(curr[2]); + const n = parseInt(curr[2], 10); let mult; if (Object.hasOwn(map, curr[3])) { mult = map[curr[3]]; @@ -150,10 +153,10 @@ export function parseDuration(str) { /** returns whether the given date is in Daylight Saving Time in the named timezone */ export function isDst(date, timezone) { - if (typeof date === "undefined" || Number.isNaN(date)) {return undefined;} - if (typeof date === "number" || typeof date === "string") {date = new Date(date);} + if (typeof date === "undefined" || Number.isNaN(date)) { return undefined; } + if (typeof date === "number" || typeof date === "string") { date = new Date(date); } - const dateFormat = Intl.DateTimeFormat("en-CA", {timeZone: timezone, timeZoneName: "shortOffset"}); + const dateFormat = Intl.DateTimeFormat("en-CA", { timeZone: timezone, timeZoneName: "shortOffset" }); function wholeHourOffset(d) { // returns the whole hour part of the UTC offset as an integer. @@ -165,16 +168,20 @@ export function isDst(date, timezone) { const year = date.getUTCFullYear(); const janOffset = wholeHourOffset(new Date(Date.UTC(year, 0, 1))); const julOffset = wholeHourOffset(new Date(Date.UTC(year, 6, 1))); - if (janOffset === julOffset) {return false;} + if (janOffset === julOffset) { return false; } const currentOffset = wholeHourOffset(date); const dstOffset = Math.max(janOffset, julOffset); return currentOffset === dstOffset; } -/** calculates the cycleNumber (number of resets since the reference time) of the given task at the given time (a Date object or timestamp) */ -export function calcCycleNumber(task, time) { +/** + * Calculates the reset count (number of resets since the reference time) of the given task + * at the given time (a Date object or timestamp). + * Uses the task definition ref by default, if it has one. If you need to use the cycle ref, pass it as `altRef`. +*/ +export function calcResetCount(task, time, altRef = undefined) { time = new Date(time); - const ref = new Date(task.ref || 0); + const ref = new Date(altRef || task.ref || 0); const period = parseDuration(task.period); let diff = time.getTime() - ref.getTime(); if (task.observesDst && isDst(time, C.SERVER_TIMEZONE)) { @@ -196,20 +203,20 @@ export function calcTaskTimes(task, date) { date = new Date(date); const ref = new Date(task.ref || 0); const period = parseDuration(task.period); - const cycleNumber = calcCycleNumber(task, date); - const prevResetTimestamp = ref.getTime() + (cycleNumber * period); + const resetCount = calcResetCount(task, date); + const prevResetTimestamp = ref.getTime() + (resetCount * period); let nextResetTimestamp = prevResetTimestamp + period; let thisCycleLeaveTimestamp = prevResetTimestamp + parseDuration(task.duration); if (task.observesDst) { - if (isDst(nextResetTimestamp, C.SERVER_TIMEZONE)) {nextResetTimestamp -= C.MILLISECONDS_PER_HOUR;} - if (isDst(thisCycleLeaveTimestamp, C.SERVER_TIMEZONE)) {thisCycleLeaveTimestamp -= C.MILLISECONDS_PER_HOUR;} + if (isDst(nextResetTimestamp, C.SERVER_TIMEZONE)) { nextResetTimestamp -= C.MILLISECONDS_PER_HOUR; } + if (isDst(thisCycleLeaveTimestamp, C.SERVER_TIMEZONE)) { thisCycleLeaveTimestamp -= C.MILLISECONDS_PER_HOUR; } } let isAvailable = true; - if (task.duration) {isAvailable = (date.getTime() < thisCycleLeaveTimestamp);} + if (task.duration) { isAvailable = (date.getTime() < thisCycleLeaveTimestamp); } - return {nextResetTimestamp, thisCycleLeaveTimestamp, isAvailable}; + return { nextResetTimestamp, thisCycleLeaveTimestamp, isAvailable }; } export function factionIcon(name) { diff --git a/sources/js/icons.js b/sources/js/icons.js index 1cdbffd..d9f8cfb 100644 --- a/sources/js/icons.js +++ b/sources/js/icons.js @@ -1,3 +1,5 @@ +/* eslint-disable @stylistic/operator-linebreak, @stylistic/indent */ + /* Heroicons https://heroicons.com/ * MIT license https://github.com/tailwindlabs/heroicons/blob/master/LICENSE */ @@ -25,7 +27,7 @@ const eyeOutline = c-4.638 0-8.573-3.007-9.963-7.178Z" /> -` +`; const bellOutline = ` @@ -100,7 +102,7 @@ const calendarDays16 = a.75.75 0 0 0-1.5 0V3h-5V1.75A.75.75 0 0 0 4.75 1ZM3.5 7a1 1 0 0 1 1-1h7a1 1 0 0 1 1 1v4.5a1 1 0 0 1-1 1h-7 a1 1 0 0 1-1-1V7Z" /> -` +`; /* Lucide icons https://github.com/lucide-icons/lucide @@ -110,11 +112,13 @@ const calendarDays16 = // https://icon-sets.iconify.design/lucide/skip-forward/ const skipForward = ` - -` +`; + +/* eslint-enable @stylistic/indent */ export { chevronDownOutline as collapseIcon, eyeSlashOutline as hideIcon, diff --git a/sources/js/moreInfo.js b/sources/js/moreInfo.js index e213152..24846fb 100644 --- a/sources/js/moreInfo.js +++ b/sources/js/moreInfo.js @@ -12,7 +12,7 @@ const factions = `

The Faction Syndicates are ${factionIcon("FactionSigilBusiness.png")}The Perrin Sequence, ${factionIcon("FactionSigilAssassins.png")}Red Veil, and ${factionIcon("FactionSigilChurch.png")}New Loka.

-

Pledge your loyalty to a faction syndicate at the Syndicates console in your ${C.BASE_OF_OPERATIONS_TOOLTIP}.

` +

Pledge your loyalty to a faction syndicate at the Syndicates console in your ${C.BASE_OF_OPERATIONS_TOOLTIP}.

`; export default { daily_first_win_bonus: `

Your first completed mission after the daily reset gives double base credit rewards. This only applies to the end-of-mission bonus, and does not apply to credits picked up in the mission.

@@ -70,5 +70,5 @@ export default {

Each week, a random planet will reward double bonus Vault resources (and double reward track progress) for missions played there.

Check the current boosted planet and track your reward progress in the Clan menu.

Note: You can use the "Clan Only" matchmaking option to join squads with your Clan or Alliance members, but playing with Alliance members does not give bonus Vault resources or reward progress.

-

* Descendia missions do not contribute to Clan Weekly Initiatives.

` -} +

* Descendia missions do not contribute to Clan Weekly Initiatives.

`, +}; diff --git a/sources/tests/app.test.js b/sources/tests/app.test.js index 49fde38..f5f35b3 100644 --- a/sources/tests/app.test.js +++ b/sources/tests/app.test.js @@ -9,7 +9,7 @@ beforeEach(() => { afterEach(() => { vi.useRealTimers(); -}) +}); describe("displayOtherTaskCountdown({period, duration, ref, observesDst})", () => { beforeEach(() => { @@ -50,9 +50,9 @@ describe("displayOtherTaskCountdown({period, duration, ref, observesDst})", () = ["14d", "2d", "2022-12-30T09:00:00-05:00", true, "2024-11-03T09:00:00-05:00", "(Available in 12d 00:00:00)"], ])("(%s, %s, %s, %s) @ %s -> %s", ([period, duration, ref, observesDst, date, countdown]) => { vi.setSystemTime(date); - app.displayOtherTaskCountdown({id: "other_test", period, duration, ref, observesDst}); + app.displayOtherTaskCountdown({ id: "other_test", period, duration, ref, observesDst }); expect(document.querySelector(".other-countdown").textContent).toEqual(countdown); - }) + }); }); describe("getTaskById", () => { @@ -64,8 +64,8 @@ describe("getTaskById", () => { ["weekly_netracells", "NetraRequiemIcon.png"], // 2nd level weekly ["daily_kim_hex_parent", "KIM/RetroPfpG.png"], // both a parent and child ["daily_kim_kaya", "KIM/RetroPfpH.png"], // 3rd level task - ["fake_task_id", undefined] + ["fake_task_id", undefined], ])("%s", ([id, icon]) => { expect(app.getTaskById(id)?.icon).toEqual(icon); - }) + }); }); diff --git a/sources/tests/cycles.schema.json b/sources/tests/cycles.schema.json index 5c12a1f..0d4dca7 100644 --- a/sources/tests/cycles.schema.json +++ b/sources/tests/cycles.schema.json @@ -54,7 +54,7 @@ "type": "string" }, "iconFilter": { - "description": "set to `true` to convert the `icon` to monochrome black or white depending on the theme. Otherwise, it will be used in color as-is. Note the opposite name and default bahaviour to `tasks.json`", + "description": "set to `true` to convert the `icon` to monochrome black or white depending on the theme. Otherwise, it will be used in color as-is. Note the opposite name and default behaviour to `tasks.json`", "type": "boolean", "default": false } diff --git a/sources/tests/functions.test.js b/sources/tests/functions.test.js index dbb78d6..7cc162e 100644 --- a/sources/tests/functions.test.js +++ b/sources/tests/functions.test.js @@ -47,7 +47,7 @@ describe("Daylight Saving Time", () => { ["2025-10-05T01:59:59+10:30", "Australia/Lord_Howe", false], // just before start ["2025-10-05T02:00:00+10:30", "Australia/Lord_Howe", true], // just after start ["2025-10-05T03:00:00+11:00", "Australia/Lord_Howe", true], // just after start - ])(`isDst(%s, %s) -> %o`, ([d, tz, expected]) => { + ])("isDst(%s, %s) -> %o", ([d, tz, expected]) => { expect(isDst(new Date(d), tz)).toBe(expected); expect(isDst(new Date(d).getTime(), tz)).toBe(expected); expect(isDst(d, tz)).toBe(expected); @@ -63,8 +63,8 @@ describe("parseDuration", () => { ["12h", 43_200_000], ["14d", 1_209_600_000], ["2d3H10M12s", 184_212_000], - ["1m, 359h 1d 3d, 5m6s 4h", 1_652_766_000] - ])(`parseDuration(%s) -> %i`, ([d, expected]) => { + ["1m, 359h 1d 3d, 5m6s 4h", 1_652_766_000], + ])("parseDuration(%s) -> %i", ([d, expected]) => { expect(parseDuration(d)).toEqual(expected); }); @@ -75,8 +75,8 @@ describe("parseDuration", () => { test.for([ ["1s 10n", 1, 1000], ["10y5w2d3h8x10m12s13g", 4, 184_212_000], - ["1m, 359h 9p1d 3d, 5m6s 4h\t1234v", 2, 1_652_766_000] - ])(`parseDuration(%s) warn %i`, ([d, warnings, expected]) => { + ["1m, 359h 9p1d 3d, 5m6s 4h\t1234v", 2, 1_652_766_000], + ])("parseDuration(%s) warn %i", ([d, warnings, expected]) => { const consoleWarn = vi.spyOn(console, "warn"); expect(parseDuration(d)).toEqual(expected); expect(consoleWarn).toHaveBeenCalledTimes(warnings); diff --git a/sources/tests/tasks.test.js b/sources/tests/tasks.test.js index 9988ade..99142fa 100644 --- a/sources/tests/tasks.test.js +++ b/sources/tests/tasks.test.js @@ -2,10 +2,10 @@ import { describe, expect, test } from "vitest"; import Ajv from "ajv/dist/2020"; import addFormats from "ajv-formats"; -import tasks from "../js/tasks.json" with {type: "json"}; -import tasks_schema from "./tasks.schema.json" with {type: "json"}; -import cycles from "../js/cycles.json" with {type: "json"}; -import cycles_schema from "./cycles.schema.json" with {type: "json"}; +import tasks from "../js/tasks.json" with { type: "json" }; +import tasks_schema from "./tasks.schema.json" with { type: "json" }; +import cycles from "../js/cycles.json" with { type: "json" }; +import cycles_schema from "./cycles.schema.json" with { type: "json" }; import moreInfo from "../js/moreInfo"; const ajv = new Ajv({ allErrors: true }); @@ -14,10 +14,10 @@ addFormats(ajv); const validate_tasks_schema = ajv.compile(tasks_schema); const validate_cycles_schema = ajv.compile(cycles_schema); -describe("valildate task definitions", () => { +describe("valildate task definitions", () => { /* eslint-disable-line max-lines-per-function */ test.for([ ["tasks", validate_tasks_schema, tasks], - ["cycles", validate_cycles_schema, cycles] + ["cycles", validate_cycles_schema, cycles], ])("validate %s against schema", ([which, validation_function, data]) => { const valid = validation_function(data); if (!valid) { console.error(validation_function.errors); } @@ -26,8 +26,8 @@ describe("valildate task definitions", () => { }); // flatten the nested tree of [sub]tasks into an array - let stack = []; - let flatTasks = []; + const stack = []; + const flatTasks = []; for (const section in tasks) { for (const t of tasks[section]) { stack.push(t); @@ -46,7 +46,7 @@ describe("valildate task definitions", () => { const task_ids = flatTasks.map((t) => t.id); test("verify unique task ids", () => { - let used_ids = []; + const used_ids = []; for (const id of task_ids) { expect(used_ids, "task ids must be unique").not.toContain(id); used_ids.push(id); @@ -58,7 +58,7 @@ describe("valildate task definitions", () => { describe("verify equal `order` lengths", () => { test.for(cycle_keys_for_test)("%s", ([task_id]) => { for (const col of cycles[task_id].columns) { - expect(col.order.length, "column lengths are not equal").toEqual(cycles[task_id].columns[0].order.length) + expect(col.order.length, "column lengths are not equal").toEqual(cycles[task_id].columns[0].order.length); } }); }); @@ -69,6 +69,28 @@ describe("valildate task definitions", () => { }); }); + describe("check cycle refs", () => { + test.for(cycle_keys_for_test)("%s", ([task_id]) => { + const section = task_id.split("_")[0]; + const ref_text = cycles[task_id].ref; + const ref = new Date(ref_text); + + const task_def = flatTasks.find((t) => t.id === task_id); + if (task_def.ref) { // cycle ref for alternate ref task must match task's alternate ref + expect(ref, `cycle ref ${ref_text} does not equal task ref ${task_def.ref}`).toEqual(new Date(task_def.ref)); + } else if (["daily", "weekly"].includes(section)) { // cycle for standard ref tasks must match standard resets + if (section === "weekly") { + expect(ref.getUTCDay(), `cycle ref ${ref_text} is not a Monday`).toEqual(1); + } + + expect( + [ref.getUTCHours(), ref.getUTCMinutes(), ref.getUTCSeconds(), ref.getUTCMilliseconds()], + `cycle ref ${ref_text} is not midnight`, + ).toEqual([0, 0, 0, 0]); + } + }); + }); + describe("validate moreInfo", () => { test.for(Object.keys(moreInfo).map((i) => [i]))("%s", ([task_id]) => { expect(task_ids, "moreInfo keys must be task ids from `tasks.json`").toContain(task_id); diff --git a/sources/tests/version.test.js b/sources/tests/version.test.js index 4779748..6dac8a3 100644 --- a/sources/tests/version.test.js +++ b/sources/tests/version.test.js @@ -12,6 +12,6 @@ describe("check version numbers", () => { test("SECURITY.md", () => { const security = readFileSync(path.resolve(__dirname, "../../SECURITY.md"), "utf-8"); const major = security.match(/\| Version \| Supported\s*\|[\r\n]*\| -* \| -* \|[\r\n]*\| (\d+)\.x\.x/)[1]; - expect(major, "SECURITY.md version number does not match app.js APP_VERSION major version number").toEqual(APP_VERSION.split(".")[0]) + expect(major, "SECURITY.md version number does not match app.js APP_VERSION major version number").toEqual(APP_VERSION.split(".")[0]); }); }); diff --git a/stylelint.config.mjs b/stylelint.config.mjs new file mode 100644 index 0000000..4fa8f81 --- /dev/null +++ b/stylelint.config.mjs @@ -0,0 +1,45 @@ +/** @type {import("stylelint").Config} */ +export default { + "referenceFiles": ["sources/**/*.css"], + "extends": [ + "stylelint-config-standard", + "@stylistic/stylelint-config", + ], + "reportInvalidScopeDisables": true, + "reportNeedlessDisables": true, + "reportUnscopedDisables": true, + "rules": { + "font-family-name-quotes": "always-unless-keyword", + "value-keyword-case": ["lower", { "ignoreKeywords": ["currentColor"] }], + "color-hex-length": null, + "property-no-vendor-prefix": [true, { "severity": "warning" }], + "selector-no-deprecated": true, + "no-descending-specificity": [true, { "severity": "warning" }], + "selector-no-invalid": true, + "no-unknown-animations": true, + "no-unknown-custom-media": true, + "no-unknown-custom-properties": true, + "unit-no-unknown": true, + "at-rule-property-required-list": { + "font-face": ["font-family", "src", "font-style", "font-weight", "font-display"], + }, + "color-named": ["always-where-possible", { "severity": "warning" }], + "declaration-no-important": [true, { "severity": "warning" }], + "unit-disallowed-list": ["ms"], // use "s" instead + "font-weight-notation": "numeric", + // complexity + "max-nesting-depth": [3, { "severity": "warning" }], + "selector-max-compound-selectors": [5, { "severity": "warning" }], + // empty lines + "rule-empty-line-before": null, + "at-rule-empty-line-before": null, + "comment-empty-line-before": null, + "declaration-empty-line-before": null, + "custom-property-empty-line-before": null, + // stylistic + "@stylistic/indentation": [4, { "ignore": "value" }], + "@stylistic/declaration-colon-newline-after": null, + "@stylistic/max-line-length": null, + "@stylistic/unicode-bom": "never", + }, +}; diff --git a/vite.config.js b/vite.config.js index c4d7e3d..68cac98 100644 --- a/vite.config.js +++ b/vite.config.js @@ -11,7 +11,7 @@ export default defineConfig(({ mode }) => ({ cacheDir: "../.vite", plugins: [ - mode === "release" ? viteSingleFile({removeViteModuleLoader: true}) : null, + mode === "release" ? viteSingleFile({ removeViteModuleLoader: true }) : null, ], build: { @@ -24,7 +24,7 @@ export default defineConfig(({ mode }) => ({ restoreMocks: true, coverage: { reportsDirectory: "../.coverage", - exclude: ["img/", "*.json"] - } - } + exclude: ["img/", "*.json"], + }, + }, }));