diff --git a/.github/workflows/README.md b/.github/workflows/README.md index a21fd054..21028c46 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -10,8 +10,8 @@ Whenever someone pushes to the `deploy` branch, this script will build Hydrant f This runs two checks: -- Prettier for formatting -- ESLint linting + TypeScript typechecking (bundled as a single check) +- Oxfmt for formatting +- Oxlint for linting + TypeScript typechecking (bundled as a single check) This pipeline does NOT trigger on pull requests or commits that only affect backend-related code, documentation, or JSON/TOML data. diff --git a/.github/workflows/ci-frontend.yml b/.github/workflows/ci-frontend.yml index 71f46ac2..ff8fabf9 100644 --- a/.github/workflows/ci-frontend.yml +++ b/.github/workflows/ci-frontend.yml @@ -22,7 +22,7 @@ on: jobs: prettier: - name: Prettier + name: Format runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 @@ -33,8 +33,8 @@ jobs: cache: "npm" - name: Install node dependencies run: npm ci - - name: Run Prettier - run: npm run ci-prettier + - name: Run Format + run: npm run format:check lint: name: Lint + Typecheck runs-on: ubuntu-latest @@ -47,10 +47,8 @@ jobs: cache: "npm" - name: Install node dependencies run: npm ci - - name: Check types - run: npm run typecheck - - name: Run ESLint - run: npm run ci-eslint + - name: Run Lint + Typecheck + run: npm run lint test: name: Unit tests runs-on: ubuntu-latest diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 9ae92dc7..39dfe89b 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -42,8 +42,6 @@ jobs: pip install . - name: Run scrapers run: python3 -m scrapers - - name: Check/generate types - run: npm run typecheck - name: Build run: npm run build env: diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 46f5006a..00000000 --- a/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -public/* diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 02146ee7..ab486c1a 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["vitest.explorer", "ms-python.python"] + "recommendations": ["vitest.explorer", "ms-python.python", "oxc.oxc-vscode"] } diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index 0c6c1bf1..00000000 --- a/eslint.config.js +++ /dev/null @@ -1,86 +0,0 @@ -import js from "@eslint/js"; -import globals from "globals"; - -import tseslint from "typescript-eslint"; -import eslintConfigPrettier from "eslint-config-prettier"; -// import reactHooks from "eslint-plugin-react-hooks"; -import vitest from "@vitest/eslint-plugin"; -import { reactRefresh } from "eslint-plugin-react-refresh"; - -import { defineConfig, includeIgnoreFile } from "eslint/config"; -import { fileURLToPath } from "node:url"; - -const gitignorePath = fileURLToPath(new URL(".gitignore", import.meta.url)); - -export default defineConfig( - includeIgnoreFile(gitignorePath, { gitignoreResolution: true }), - js.configs.recommended, - tseslint.configs.strictTypeChecked, - tseslint.configs.stylisticTypeChecked, - // reactHooks.configs.flat.recommended, - reactRefresh.configs.vite({ - // allow react router exports - allowExportNames: [ - "middleware", - "clientMiddleware", - "loader", - "clientLoader", - "action", - "clientAction", - "headers", - "handle", - "links", - "meta", - "shouldRevalidate", - ], - }), - eslintConfigPrettier, - { - languageOptions: { - ecmaVersion: 2022, - globals: globals.browser, - parserOptions: { - projectService: { - allowDefaultProject: ["*.js"], - }, - tsconfigRootDir: import.meta.dirname, - }, - }, - rules: { - "@typescript-eslint/no-unused-vars": [ - "error", - { - args: "all", - argsIgnorePattern: "^_", - caughtErrors: "all", - caughtErrorsIgnorePattern: "^_", - destructuredArrayIgnorePattern: "^_", - varsIgnorePattern: "^_", - ignoreRestSiblings: true, - }, - ], - "@typescript-eslint/consistent-type-exports": "warn", - "@typescript-eslint/consistent-type-imports": "warn", - "@typescript-eslint/switch-exhaustiveness-check": "error", - }, - }, - { - files: ["tests/**"], - plugins: { - vitest, - }, - rules: { - ...vitest.configs.recommended.rules, - }, - settings: { - vitest: { - typecheck: true, - }, - }, - languageOptions: { - globals: { - ...vitest.environments.env.globals, - }, - }, - }, -); diff --git a/oxfmt.config.ts b/oxfmt.config.ts new file mode 100644 index 00000000..dedb42d9 --- /dev/null +++ b/oxfmt.config.ts @@ -0,0 +1,27 @@ +import { defineConfig } from "oxfmt"; + +// uses .editorconfig by default, no need to customize much +export default defineConfig({ + ignorePatterns: ["public/*"], + sortImports: { + newlinesBetween: true, + customGroups: [ + { + groupName: "react-libs", + elementNamePattern: ["react", "react-**"], + }, + ], + groups: [ + "react-libs", + "type-import", + ["value-builtin", "value-external"], + "type-internal", + "value-internal", + ["type-parent", "type-sibling", "type-index"], + ["value-parent", "value-sibling", "value-index"], + "style", + "unknown", + ], + }, + sortPackageJson: { sortScripts: true }, +}); diff --git a/oxlint.config.ts b/oxlint.config.ts new file mode 100644 index 00000000..e841167a --- /dev/null +++ b/oxlint.config.ts @@ -0,0 +1,55 @@ +import { defineConfig } from "oxlint"; + +export default defineConfig({ + plugins: ["typescript", "react", "unicorn", "import", "react-perf"], + categories: { + correctness: "error", + suspicious: "warn", + }, + options: { + typeAware: true, + typeCheck: true, + reportUnusedDisableDirectives: "error", + }, + env: { + builtin: true, + es2023: true, + browser: true, + }, + ignorePatterns: ["src/components/ui/**", "src/emotion/**"], + rules: { + "react-in-jsx-scope": "off", + "no-unassigned-import": [ + "error", + { allow: ["**/*.css", "temporal-polyfill/global"] }, + ], + "react/only-export-components": [ + "warn", + { + allowConstantExport: true, + allowExportNames: [ + "middleware", + "clientMiddleware", + "loader", + "clientLoader", + "action", + "clientAction", + "headers", + "handle", + "links", + "meta", + "shouldRevalidate", + ], + }, + ], + "react/rules-of-hooks": "error", + }, + overrides: [ + { + files: ["tests/**"], + env: { vitest: true }, + + plugins: ["vitest"], + }, + ], +}); diff --git a/package-lock.json b/package-lock.json index b5d6953b..26826f42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "hydrant", "version": "0.1.0", + "hasInstallScript": true, "dependencies": { "@chakra-ui/react": "^3.37.0", "@emotion/cache": "^11.14.0", @@ -45,15 +46,11 @@ "@types/node": "^22.20.1", "@types/react": "^19.3.0", "@types/react-dom": "19.3.0", - "@vitest/eslint-plugin": "^1.6.27", - "eslint": "^10.11.0", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-react-refresh": "^0.5.7", - "globals": "^17.12.0", "jsdom": "^30.1.0", - "prettier": "^3.9.8", - "typescript": "^6.0.3", - "typescript-eslint": "^8.70.1", + "oxfmt": "^0.63.0", + "oxlint": "^1.78.0", + "oxlint-tsgolint": "^7.0.2001", + "typescript": "^7.0.2", "vite": "^8.3.0", "vitest": "^5.0.1" }, @@ -587,30 +584,6 @@ "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/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/@chakra-ui/cli": { "version": "3.37.0", "resolved": "https://registry.npmjs.org/@chakra-ui/cli/-/cli-3.37.0.tgz", @@ -650,22 +623,6 @@ "@chakra-ui/react": ">=3.0.0-next.0" } }, - "node_modules/@chakra-ui/cli/node_modules/prettier": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", - "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, "node_modules/@chakra-ui/react": { "version": "3.37.0", "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-3.37.0.tgz", @@ -1447,76 +1404,6 @@ "node": ">=18" } }, - "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/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/js": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", @@ -1538,30 +1425,6 @@ } } }, - "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.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.3.tgz", - "integrity": "sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==", - "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.2", "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.2.tgz", @@ -1657,72 +1520,6 @@ "temporal-polyfill": "^1.0.1" } }, - "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/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/@internationalized/date": { "version": "3.12.3", "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.12.3.tgz", @@ -1787,30 +1584,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "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/@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/@msgpackr-extract/msgpackr-extract-darwin-arm64": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", @@ -1937,311 +1710,156 @@ "url": "https://github.com/sponsors/oxc-project" } }, - "node_modules/@pandacss/is-valid-prop": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@pandacss/is-valid-prop/-/is-valid-prop-1.11.1.tgz", - "integrity": "sha512-tvfThJmSw88Lgk5qVYzVckZ2FY8T376mGvP1cWUC5SR58AYm82ZKEFciDbGOQKcyN70rF9qqJBB5JVWgJo3JmA==", - "license": "MIT" - }, - "node_modules/@react-router/dev": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@react-router/dev/-/dev-8.4.0.tgz", - "integrity": "sha512-GKznpn6h8c52xGXwtsypD5ZwNiWFSeqoKPdyGq7SfG+T9rFjGO8F0i/R/yECfZqHXaNz1NVmpODuBamBy9FfMg==", + "node_modules/@oxfmt/binding-android-arm-eabi": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.63.0.tgz", + "integrity": "sha512-YmRth4ZPGgEXcgmkhvANbC9uD67dxmSobW7DQuyt5tOBOKvPnIpk5SVHBj88E+7wMNRI2FhqaDbOhQFBix+b8A==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/core": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7", - "@react-router/node": "8.4.0", - "@remix-run/node-fetch-server": "^0.14.1", - "babel-dead-code-elimination": "^1.0.12", - "chokidar": "^5.0.0", - "dedent": "^1.7.2", - "es-module-lexer": "^2.1.0", - "exit-hook": "5.1.0", - "isbot": "^5.1.40", - "jsesc": "3.1.0", - "lodash": "^4.18.1", - "p-map": "^7.0.4", - "pathe": "^2.0.3", - "picocolors": "^1.1.1", - "pkg-types": "^2.3.1", - "react-refresh": "^0.18.0", - "semver": "^7.8.1", - "tinyglobby": "^0.2.16", - "valibot": "^1.4.1" - }, - "bin": { - "react-router": "bin.cjs" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=22.22.0" - }, - "peerDependencies": { - "@react-router/serve": "^8.4.0", - "@vitejs/plugin-rsc": "~0.5.26", - "react-router": "^8.4.0", - "react-server-dom-webpack": "^19.2.7", - "typescript": "^5.1.0 || ^6.0.0 || ^7.0.0", - "vite": "^7.0.0 || ^8.0.0", - "wrangler": "^4.0.0" - }, - "peerDependenciesMeta": { - "@react-router/serve": { - "optional": true - }, - "@vitejs/plugin-rsc": { - "optional": true - }, - "react-server-dom-webpack": { - "optional": true - }, - "typescript": { - "optional": true - }, - "wrangler": { - "optional": true - } + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@react-router/dev/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "node_modules/@oxfmt/binding-android-arm64": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.63.0.tgz", + "integrity": "sha512-icbahX8X2X3sRamOMecvdYeZXWjPDazRDIfvWfy7Ca1nc/ZDT2Y9k5Nt7s46EqFd7NQPdgk+CM3/SgIT5LPCaQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@react-router/fs-routes": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@react-router/fs-routes/-/fs-routes-8.4.0.tgz", - "integrity": "sha512-CIEYxrapbr6iO589fBmN7/RBJ10H8X/Tl2i7MNkuJZQ9W2Z3wPifPCHv9bFR/+FlmzeLXJlYyHyUgaCkgOIZYg==", + "node_modules/@oxfmt/binding-darwin-arm64": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.63.0.tgz", + "integrity": "sha512-WV+Ze5v5gI2qoj8jpAovt8KBTW8pjEz/AiMXXjeTQS+Bmf/MmZXTS40S8xNPDszX+W8WDv2Bbk6qKrMTtUGu1A==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "minimatch": "^10.2.5" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=22.22.0" - }, - "peerDependencies": { - "@react-router/dev": "^8.4.0", - "typescript": "^5.1.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@react-router/node": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@react-router/node/-/node-8.4.0.tgz", - "integrity": "sha512-1teZHtk7XDm1pteQ2Plgtvtjjup5C5sIwFlOMYVIyoOH7X3bId3Qb//kUdbydX6NRIxcGK28UnfZlz4SrD/mkg==", - "license": "MIT", - "dependencies": { - "@remix-run/node-fetch-server": "^0.14.1" - }, - "engines": { - "node": ">=22.22.0" - }, - "peerDependencies": { - "react-router": "8.4.0", - "typescript": "^5.1.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@remix-run/node-fetch-server": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@remix-run/node-fetch-server/-/node-fetch-server-0.14.1.tgz", - "integrity": "sha512-pKenF5ysIN5lDCnCyvoUxDhKP0tfnGagvGbZh01xxHG6DJXSIXWVC9NpLSDGjxcxd1CKdCOKuZFleXBF3pChIg==", - "license": "MIT" - }, - "node_modules/@remix-run/route-pattern": { - "version": "0.22.1", - "resolved": "https://registry.npmjs.org/@remix-run/route-pattern/-/route-pattern-0.22.1.tgz", - "integrity": "sha512-czdGJWh09Lapvcqt7Vb09KlrU2PhRJ8xQaI+AItTuD9aDJ5MAgxnS38lnys6Ll+6RJEqPiaUqTwIQhVLwFvv+w==", - "license": "MIT" - }, - "node_modules/@rjsf/chakra-ui": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@rjsf/chakra-ui/-/chakra-ui-6.10.1.tgz", - "integrity": "sha512-nMTQdRzCAyqhuEVoHr3TC/Z5323I74m/APN1wfYAd8SEoKq0D42REZUgNdyrnmtC2e8YZUPZ489Ho2/e7BOukw==", - "license": "Apache-2.0", - "dependencies": { - "@emotion/cache": "^11.14.0", - "@emotion/is-prop-valid": "^1.4.0", - "@emotion/weak-memoize": "^0.4.0", - "lucide-react": "^1.21.0", - "react-icons": "^5.6.0" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@chakra-ui/react": ">=3.16.1", - "@rjsf/core": "^6.10.1", - "@rjsf/utils": "^6.10.1", - "chakra-react-select": ">=6", - "react": ">=18" - } - }, - "node_modules/@rjsf/core": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@rjsf/core/-/core-6.10.1.tgz", - "integrity": "sha512-LHLbXYEq1+c2CpynBlf5JBlUhPNxOkVV+txf1BvamgJSt4QOisXFw5Uv+mBdLRi4BI+FfT0ofMVOgGWnuwG7gw==", - "license": "Apache-2.0", - "dependencies": { - "markdown-to-jsx": "^9.8.2" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@rjsf/utils": "^6.10.1", - "react": ">=18" - } - }, - "node_modules/@rjsf/utils": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-6.10.1.tgz", - "integrity": "sha512-dlbQUCoTl/+/TenyYs1zAKXf9sLTebtY+6pB+bUFsaN4xkdAtR8YBLwmpa1N5yNiRzf3fbxveISNuZmDrZE/yQ==", - "license": "Apache-2.0", - "dependencies": { - "@x0k/json-schema-merge": "^1.0.6", - "fast-equals": "^6.0.0", - "fast-uri": "^4.1.4", - "jsonpointer": "^5.0.1", - "react-is": "^18.3.1" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "react": ">=18" - } - }, - "node_modules/@rjsf/validator-ajv8": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-6.10.1.tgz", - "integrity": "sha512-Jyim45UKd39SgWn4ryaydTigZ0dddNEeqZUfTlLgsy2v23uMDLyeKjNCItC09dy00dtKzwM3Q4WA0FtQ5IjOgA==", - "license": "Apache-2.0", - "dependencies": { - "ajv": "^8.20.0", - "ajv-formats": "^2.1.1" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@rjsf/utils": "^6.10.1" - } - }, - "node_modules/@rolldown/binding-android-arm-eabi": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.9.tgz", - "integrity": "sha512-tNISae1QEf/vkb3xkRcjV5SEdzPE97We5IVaa2Z8jSszQPZ8U60B/YCYpw4QI7VidYsBtKavczXf+DyDs9WGxw==", + "node_modules/@oxfmt/binding-darwin-x64": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.63.0.tgz", + "integrity": "sha512-CJGSBdDxXOWIpoFXHpverimCvz084KA7L483rqJ44c3jDtzv6d4qOSoR/V9ywSHfV+Ks1lwIj2P49BFhunLNAA==", "cpu": [ - "arm" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "android" + "darwin" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.9.tgz", - "integrity": "sha512-YC8YsI30o606GTZi0VyzYlsDKFP8W61i/QzayHDkLbNEz/IShqAmTa+hsJRj13xTHA0H+6fk4b2UmGn+Q/cMlg==", + "node_modules/@oxfmt/binding-freebsd-x64": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.63.0.tgz", + "integrity": "sha512-BDfKY+KhL2078cgswBBFQPAYuxCy93bS/iC5frdSeSbTLcGrR6VC2hsuPTanoJmg84+wSyWl0wWC1eR+uTnkRg==", "cpu": [ - "arm64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "android" + "freebsd" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.9.tgz", - "integrity": "sha512-IwhlH3qK5urrY8hZiEgGkHKEFN901p/p2bjxCxJlr4GyNnF7wYpUvK+Y43uaRYuC4hpfjzbR3SJC3arX1jGvmw==", + "node_modules/@oxfmt/binding-linux-arm-gnueabihf": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.63.0.tgz", + "integrity": "sha512-Ov1cQEXT4mj7cojAokWSS1eoxkoyvbDfAbxNsGIKY2o36kvdAaFzPxRN6NxFRk9fD72B8oCoTTX/NuYTUWlpsg==", "cpu": [ - "arm64" + "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.9.tgz", - "integrity": "sha512-XxpJfVzFh+jilRxIXUqcfYAYcunIc/XEzIizsOL1fcJee5Sf7H3mH8WlLmfHfluz5amqR88QQo9izKtmMlavAw==", + "node_modules/@oxfmt/binding-linux-arm-musleabihf": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.63.0.tgz", + "integrity": "sha512-0LE7ro3+6L79jcMANycAZfRaC7zxr9YZ2+vEL5uMD9QlEep+rS/r1kSJsnuLl991NXJZD60euh0PC1GHrR20vw==", "cpu": [ - "x64" + "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.9.tgz", - "integrity": "sha512-kSfvhmgeWyfkbT3p/1s5vSgboogoah2zkm9fX2zjg2hHxSV7T4KhMWRUUaRk4OXNqoD3QAUeRqLcs1aZOK4U1g==", + "node_modules/@oxfmt/binding-linux-arm64-gnu": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.63.0.tgz", + "integrity": "sha512-izPk+2Z4gjuZK32Fqh5qXoMpT/2NXzLh++ob57HiEiVSQZ1iYXu8EKMzb+K5AvWyIEXhdDIt7ADjGGtFhkT9Bw==", "cpu": [ - "x64" + "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ - "freebsd" + "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.9.tgz", - "integrity": "sha512-1RVzG17pxqbTfYLC352JlLt6kKLG+6Hr30n8DlIJqsnV5luUDd2Qdx9Ayw1Cabfyb1K9k0jXEZ7evxkRoT+uiw==", + "node_modules/@oxfmt/binding-linux-arm64-musl": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.63.0.tgz", + "integrity": "sha512-alPmbOuWXFXiSo+lOtv6X71C7SYMEDW2WVvywOvf9BwKgEhSNGhMTLeFVSjKUMCamcjbbgVdsWF8GN1uy8xshg==", "cpu": [ - "arm" + "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2251,12 +1869,12 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.9.tgz", - "integrity": "sha512-BXqPvZ2drqVD+/Z8UpKwcs4Mp7grM+eGFku4CAEKrEtcbAsUpzREphK1sogCRZGreVPiMkiiBtw0n3TPteuqvw==", + "node_modules/@oxfmt/binding-linux-ppc64-gnu": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.63.0.tgz", + "integrity": "sha512-BdzCPvolJc4AWZ+YMzgUDJcDzbQWrFjYuqBHoNHNqP1aCaluQRJNs4k3vNU5IG7vTpjf9zeD73D7MFM1TecZpg==", "cpu": [ - "arm64" + "ppc64" ], "dev": true, "libc": [ @@ -2271,16 +1889,16 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.9.tgz", - "integrity": "sha512-11vWvo8YDwLzukt27J3aYDWU+gg2P7J+ZOmiJ0hkF5BXZDW7pVya7r40MXDy6ya0i9KamoENSVKIugvJNgFXIA==", + "node_modules/@oxfmt/binding-linux-riscv64-gnu": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.63.0.tgz", + "integrity": "sha512-7sIgfLzqtNKSkMGsGVyRpHwpjNezRg2XONvUOheFZs95TSZpM0JAuPpA8KrQFsWc4wPU95roX2O69JgH8igOgw==", "cpu": [ - "arm64" + "riscv64" ], "dev": true, "libc": [ - "musl" + "glibc" ], "license": "MIT", "optional": true, @@ -2291,16 +1909,16 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.9.tgz", - "integrity": "sha512-a1tijMkdwsIARtc0F39ApURROkf3NwqinI6TOiSSWCTR7dT96dffNvMUtDHnq64wKNTIZOIlzKrFvvFUznJiyw==", + "node_modules/@oxfmt/binding-linux-riscv64-musl": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.63.0.tgz", + "integrity": "sha512-9Tcg0y0WcVa6Mm9AgcgFMseDS+VkFJZpKZ8We9SpDY4gg5jewSwln+0sO04QLcTS1BtfDl9MwR+NfID8L7PUTg==", "cpu": [ - "ppc64" + "riscv64" ], "dev": true, "libc": [ - "glibc" + "musl" ], "license": "MIT", "optional": true, @@ -2311,10 +1929,10 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.9.tgz", - "integrity": "sha512-x6SQNdAvv4c3hWqTMaWuawzMX9myaCs/yEmlGsxJzkdClnHW7FbrjQuSiRDhuSYzEYoEMhsaJy9qHG/XNemJPQ==", + "node_modules/@oxfmt/binding-linux-s390x-gnu": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.63.0.tgz", + "integrity": "sha512-qWKC1pEOpx1qYhXaugPhHUeXwSfqEOk2wJH2LqVXGPV5iQYfdAZdt+d2XDiX4DTSWA2QDMUcFB+wEORh3Xn/sA==", "cpu": [ "s390x" ], @@ -2331,10 +1949,10 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.9.tgz", - "integrity": "sha512-9s0AZ8BFK5/n7B/TBoa2yJE3gI3KURrbXcPBlsAsvjU4VeJKgE90y1YtNxyEUIcHPQkg6/yfF3qihUrcM/Kf0Q==", + "node_modules/@oxfmt/binding-linux-x64-gnu": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.63.0.tgz", + "integrity": "sha512-S9wXYOiGSqYGS4Fx/TFsY+xDd/7dE5s+rUgbA4TsHiVF9e8J3ZcKmP7dsP/7iqLI9Wz7Ic7TzEr3mdthRCTdrA==", "cpu": [ "x64" ], @@ -2351,10 +1969,10 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.9.tgz", - "integrity": "sha512-P7VWAmV+WdJluH7ovnRGoiv2i8To7GAZ+kGzfGup635cyL7SyYl3lSUaA3Gp5THf0n/Co5EyEqb2zbqq+nMOHQ==", + "node_modules/@oxfmt/binding-linux-x64-musl": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.63.0.tgz", + "integrity": "sha512-5eGyTJuMZNwBSHCivXt8Yuta6GeTYksOPXRk2MIhajiyFGQx7bjaHIwY+ZusAoFHhT157A9x6sktLjYo9D5oMQ==", "cpu": [ "x64" ], @@ -2371,10 +1989,10 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.9.tgz", - "integrity": "sha512-1qixtsE4BK8h+yS3BfmZ09UhA7O/N4IACva6YBr7EBvCJraByTuRcgOTaiA62Tm0vey3UcKXLOaoGHtYmNGEVg==", + "node_modules/@oxfmt/binding-openharmony-arm64": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.63.0.tgz", + "integrity": "sha512-Rz7hx+Dv3DoW/S6pwVAyjfFXp7/trdQ1zg+vNmsdsdDNlUccugp4XNqambSuEAeP0DaG9k72AtNyfDXCEg0AGw==", "cpu": [ "arm64" ], @@ -2388,10 +2006,10 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.9.tgz", - "integrity": "sha512-ok8IQjcEPs1AKZfuEUznVBrJw+gK4soq+bx8b1X2XoMqVClarc1q5JDmVtWXY1xfr6ZuHTAsPXHTgTrqKTZeww==", + "node_modules/@oxfmt/binding-win32-arm64-msvc": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.63.0.tgz", + "integrity": "sha512-T/IuizKN9mr4Xw6YYnptkXRNdLkyIlUZ7c8zfTOBpoytZyJ1BAsMUvsMDEx0X4YvSMpaivm+DR8112rQfzC25g==", "cpu": [ "arm64" ], @@ -2405,12 +2023,12 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.9.tgz", - "integrity": "sha512-Ip2mXoU0hM0boq3Rf+ekuT653OROSo6aSYcPT1VHE4q52KvyxgFkQgrgb/IEsxOuvQ2fZZbs8khJAyCEPM24/g==", + "node_modules/@oxfmt/binding-win32-ia32-msvc": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.63.0.tgz", + "integrity": "sha512-XjrO5FJ5Wl9vsAxtCP1G/eaeT6y1K2s9CICUHGE42cEjou32/J6S+B1KnrOAboj6E7uhJnwPbRSvznWcxNdA0g==", "cpu": [ - "x64" + "ia32" ], "dev": true, "license": "MIT", @@ -2422,55 +2040,988 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@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==", + "node_modules/@oxfmt/binding-win32-x64-msvc": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.63.0.tgz", + "integrity": "sha512-sgsHCQy432OTQH4Ikk3tZptp3GqwnhwUDuY0loBH41zyHWfMZY9v8Dy78wsnSofHejvFozZGgJgBB1A0LQRwMQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@swc/helpers": { - "version": "0.5.23", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", - "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "node_modules/@oxlint-tsgolint/darwin-arm64": { + "version": "7.0.2002", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/darwin-arm64/-/darwin-arm64-7.0.2002.tgz", + "integrity": "sha512-E3yYb/tI+5M3SqGgKwrwUD0nmjA2FMGD1xFbma46qi8Y7+nPTlGY6bZk/hqxijc9Vda/tdtUxQ2d5zGIEX9qlg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", + "node_modules/@oxlint-tsgolint/darwin-x64": { + "version": "7.0.2002", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/darwin-x64/-/darwin-x64-7.0.2002.tgz", + "integrity": "sha512-qEaMySdvzs8faaJwMO4dFtPFCa+degFCz+HbkFQ0REcIJqJ16KmC0smt30FAI9TeEHCoAtz6sU/6acJvi4uaxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint-tsgolint/linux-arm64": { + "version": "7.0.2002", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/linux-arm64/-/linux-arm64-7.0.2002.tgz", + "integrity": "sha512-bqPObCNIR6tDQSc0Xn9Nu7EfWRXFB+qFzADRl/q6YA8zT9YUg3pGUbmicV2hV91uLpiot74so9ocGjx35SaX6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint-tsgolint/linux-x64": { + "version": "7.0.2002", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/linux-x64/-/linux-x64-7.0.2002.tgz", + "integrity": "sha512-E5CcRALAYdiQMbdYjEqlLOiYuEdt21RtXiyxIoD8bfypdfkpWppacijKOR4i0DtLXFRVwCGQggZBWk5bOIpU1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint-tsgolint/win32-arm64": { + "version": "7.0.2002", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/win32-arm64/-/win32-arm64-7.0.2002.tgz", + "integrity": "sha512-L+ul0ZFDiz/ZmTwCQ1J/qS7S6s45ktiaFqb+ZIzx+1sSdtdRxm8/H0+p4VDQYrCKBFzKNb6VjUlm48bNEe3BUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxlint-tsgolint/win32-x64": { + "version": "7.0.2002", + "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/win32-x64/-/win32-x64-7.0.2002.tgz", + "integrity": "sha512-zkZeJfo4UmYiCzj77u85eCDpdxOiQiSAsV6j9Pvj5e7Vu5+MvwLtK1xmVutE77tJmUJ3eBOSXHR6WgY4AwHHGQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxlint/binding-android-arm-eabi": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.85.0.tgz", + "integrity": "sha512-q2KO/Zso9UT+OMn0NF9ywn4E4t0MI3yxiDhNyhsQ7DyQJrC4FhFE4TXOi4bktFnOWXTMds8qZSbpv2XwRaNOBg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-android-arm64": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.85.0.tgz", + "integrity": "sha512-SxLN3ALjoT9NNdvpjEevGeHvfzTAFrF0NBYB5tzK7/GtCKMze3j1e/m/X2ozqGj2U9hfGG/dg/OG8vpVK4PiDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-arm64": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.85.0.tgz", + "integrity": "sha512-Y/Sup/J4f0f9UGsSd/xyCNTeWL+gepO63GBdEDAfue9nBsnk9zMmnIXx1O6b1V8C90vB5nucYNZ0pbMXAp8zJA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-x64": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.85.0.tgz", + "integrity": "sha512-ApOSNC04ynpDTwvBD+//0wyfODRSbEzvRoKpX8teffmc27z8AockwSNeMXGJXn5KP85eahDgR/2llICWLkzcnw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-freebsd-x64": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.85.0.tgz", + "integrity": "sha512-bNrVrCOA/kHky3Tu79IXWXe5bhIgLXfUuUEDHlAGOHUk96MkvDZ1ecaQF19rwstrnaqfP1o9nBTqzIr9+ZHkUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-gnueabihf": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.85.0.tgz", + "integrity": "sha512-NUrzOJ1s/EqsVvfn2L/1D8Wro2LPIZUbihL8kOJLh5fEdGEN3rdOGUYq3HwnUIL8sjpoP+4N6RaGrgmMJnaMPw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-musleabihf": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.85.0.tgz", + "integrity": "sha512-UJXrAT3E/RWkEqXLIs2ehETja1qfgkPb+5gwLIIS+o/6cf+grHvoOXTa5997a/YNQfcJS0DRBTOfZt95cvOI1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-gnu": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.85.0.tgz", + "integrity": "sha512-lK40QLjI0HxigO7CjDDshEtfYIeiYS0020v5BHFPqN4uuQBQxd2K9LNom2dW15o9F1937quSCRVp4ZsVhdbYdg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-musl": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.85.0.tgz", + "integrity": "sha512-c2zbdBwGKreHXwRx3gWBuFGJxLhxgsg6YlZ+3H+RgRusU/UEV9jNwJ3HGYK+nRo0LvBa7mt6Kj86xoVotUo8cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-ppc64-gnu": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.85.0.tgz", + "integrity": "sha512-tlt/Hy8lZ97/lCPmCgw/B3k/mwh+BzaIPbPkldZEly7TwLmx0xe2CQcaW2g/rR0dOgS9JNGCZsMEqLhUNMGvaw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-gnu": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.85.0.tgz", + "integrity": "sha512-3tNR9Xey82X0zKuY1d8hJ6Rc9gwRDurmqGLnQZa5xqOXy8/YyiqFXjAtugkKLY82obOlpK1eSiDRlgcNPuxtIg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-musl": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.85.0.tgz", + "integrity": "sha512-wbGRd5PqCcjkJFHhZuZ2OBSUQY9czlQsoA/cQQB9JK/L9mC5MQgGoKAh+xd8QjA5V+0D3j+Qd1lAWn1I8zlelA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-s390x-gnu": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.85.0.tgz", + "integrity": "sha512-3Sn0kSrE4DPZCWV/8o+n4x3aFZxI9ulMnkYlwCbJ8eUVkwRK2IerohE/A/z3SNbCwoPFOCJmGE5Avrq0rrvdvQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-gnu": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.85.0.tgz", + "integrity": "sha512-JY2pxxYfB62bAGfejljVCqc44etItehPuAyaeSAdMuEMtwNA00ggMnS66lC1oIhos6oOXUkuU6mZ9bpFh3BqWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-musl": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.85.0.tgz", + "integrity": "sha512-5k74vZ6qJBjBHEOlBk9B/iv68Yu0F1Afw/vvT2ar6OGCqEeXLaSjXz2n/IPCbhLG22UoKoYEJTzpYraRdcp6PA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-openharmony-arm64": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.85.0.tgz", + "integrity": "sha512-GbAl5qt5TCkPLXTaIISZJnugrcBhra6rodcXc9jYt620UtdsTt71NlNmJmm0frxzFpd54x/G+MkitEJA8I/BoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-arm64-msvc": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.85.0.tgz", + "integrity": "sha512-kjmws5MK0et2swk4ND85D7NVQyDHw162i6whtZDLUA/lo6FQyBZDcmMRCMcVZcNrAhIaftVb00x9ChGDOjjNJA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-ia32-msvc": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.85.0.tgz", + "integrity": "sha512-eSsIJx9n4yxvOqYTZyPEMyEXRmE60XH7xGAU7i0Qbsn1lf6Za3CWJ9aRd82oSFKXaxhp+sA6/yMJVRIpLpna6A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-x64-msvc": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.85.0.tgz", + "integrity": "sha512-pBebIPUpKKhWrhSMWhy8TdAZBewiXnfxmaAGxhzxM1068GagqFaTwgKlU6e+UyJ2sPR+VoHouhXuGJkQjsrDvA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@pandacss/is-valid-prop": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@pandacss/is-valid-prop/-/is-valid-prop-1.11.1.tgz", + "integrity": "sha512-tvfThJmSw88Lgk5qVYzVckZ2FY8T376mGvP1cWUC5SR58AYm82ZKEFciDbGOQKcyN70rF9qqJBB5JVWgJo3JmA==", + "license": "MIT" + }, + "node_modules/@react-router/dev": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@react-router/dev/-/dev-8.4.0.tgz", + "integrity": "sha512-GKznpn6h8c52xGXwtsypD5ZwNiWFSeqoKPdyGq7SfG+T9rFjGO8F0i/R/yECfZqHXaNz1NVmpODuBamBy9FfMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@react-router/node": "8.4.0", + "@remix-run/node-fetch-server": "^0.14.1", + "babel-dead-code-elimination": "^1.0.12", + "chokidar": "^5.0.0", + "dedent": "^1.7.2", + "es-module-lexer": "^2.1.0", + "exit-hook": "5.1.0", + "isbot": "^5.1.40", + "jsesc": "3.1.0", + "lodash": "^4.18.1", + "p-map": "^7.0.4", + "pathe": "^2.0.3", + "picocolors": "^1.1.1", + "pkg-types": "^2.3.1", + "react-refresh": "^0.18.0", + "semver": "^7.8.1", + "tinyglobby": "^0.2.16", + "valibot": "^1.4.1" + }, + "bin": { + "react-router": "bin.cjs" + }, + "engines": { + "node": ">=22.22.0" + }, + "peerDependencies": { + "@react-router/serve": "^8.4.0", + "@vitejs/plugin-rsc": "~0.5.26", + "react-router": "^8.4.0", + "react-server-dom-webpack": "^19.2.7", + "typescript": "^5.1.0 || ^6.0.0 || ^7.0.0", + "vite": "^7.0.0 || ^8.0.0", + "wrangler": "^4.0.0" + }, + "peerDependenciesMeta": { + "@react-router/serve": { + "optional": true + }, + "@vitejs/plugin-rsc": { + "optional": true + }, + "react-server-dom-webpack": { + "optional": true + }, + "typescript": { + "optional": true + }, + "wrangler": { + "optional": true + } + } + }, + "node_modules/@react-router/dev/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@react-router/fs-routes": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@react-router/fs-routes/-/fs-routes-8.4.0.tgz", + "integrity": "sha512-CIEYxrapbr6iO589fBmN7/RBJ10H8X/Tl2i7MNkuJZQ9W2Z3wPifPCHv9bFR/+FlmzeLXJlYyHyUgaCkgOIZYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^10.2.5" + }, + "engines": { + "node": ">=22.22.0" + }, + "peerDependencies": { + "@react-router/dev": "^8.4.0", + "typescript": "^5.1.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@react-router/node": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@react-router/node/-/node-8.4.0.tgz", + "integrity": "sha512-1teZHtk7XDm1pteQ2Plgtvtjjup5C5sIwFlOMYVIyoOH7X3bId3Qb//kUdbydX6NRIxcGK28UnfZlz4SrD/mkg==", + "license": "MIT", + "dependencies": { + "@remix-run/node-fetch-server": "^0.14.1" + }, + "engines": { + "node": ">=22.22.0" + }, + "peerDependencies": { + "react-router": "8.4.0", + "typescript": "^5.1.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@remix-run/node-fetch-server": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@remix-run/node-fetch-server/-/node-fetch-server-0.14.1.tgz", + "integrity": "sha512-pKenF5ysIN5lDCnCyvoUxDhKP0tfnGagvGbZh01xxHG6DJXSIXWVC9NpLSDGjxcxd1CKdCOKuZFleXBF3pChIg==", + "license": "MIT" + }, + "node_modules/@remix-run/route-pattern": { + "version": "0.22.1", + "resolved": "https://registry.npmjs.org/@remix-run/route-pattern/-/route-pattern-0.22.1.tgz", + "integrity": "sha512-czdGJWh09Lapvcqt7Vb09KlrU2PhRJ8xQaI+AItTuD9aDJ5MAgxnS38lnys6Ll+6RJEqPiaUqTwIQhVLwFvv+w==", + "license": "MIT" + }, + "node_modules/@rjsf/chakra-ui": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@rjsf/chakra-ui/-/chakra-ui-6.10.1.tgz", + "integrity": "sha512-nMTQdRzCAyqhuEVoHr3TC/Z5323I74m/APN1wfYAd8SEoKq0D42REZUgNdyrnmtC2e8YZUPZ489Ho2/e7BOukw==", + "license": "Apache-2.0", + "dependencies": { + "@emotion/cache": "^11.14.0", + "@emotion/is-prop-valid": "^1.4.0", + "@emotion/weak-memoize": "^0.4.0", + "lucide-react": "^1.21.0", + "react-icons": "^5.6.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@chakra-ui/react": ">=3.16.1", + "@rjsf/core": "^6.10.1", + "@rjsf/utils": "^6.10.1", + "chakra-react-select": ">=6", + "react": ">=18" + } + }, + "node_modules/@rjsf/core": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@rjsf/core/-/core-6.10.1.tgz", + "integrity": "sha512-LHLbXYEq1+c2CpynBlf5JBlUhPNxOkVV+txf1BvamgJSt4QOisXFw5Uv+mBdLRi4BI+FfT0ofMVOgGWnuwG7gw==", + "license": "Apache-2.0", + "dependencies": { + "markdown-to-jsx": "^9.8.2" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@rjsf/utils": "^6.10.1", + "react": ">=18" + } + }, + "node_modules/@rjsf/utils": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-6.10.1.tgz", + "integrity": "sha512-dlbQUCoTl/+/TenyYs1zAKXf9sLTebtY+6pB+bUFsaN4xkdAtR8YBLwmpa1N5yNiRzf3fbxveISNuZmDrZE/yQ==", + "license": "Apache-2.0", + "dependencies": { + "@x0k/json-schema-merge": "^1.0.6", + "fast-equals": "^6.0.0", + "fast-uri": "^4.1.4", + "jsonpointer": "^5.0.1", + "react-is": "^18.3.1" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@rjsf/validator-ajv8": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-6.10.1.tgz", + "integrity": "sha512-Jyim45UKd39SgWn4ryaydTigZ0dddNEeqZUfTlLgsy2v23uMDLyeKjNCItC09dy00dtKzwM3Q4WA0FtQ5IjOgA==", + "license": "Apache-2.0", + "dependencies": { + "ajv": "^8.20.0", + "ajv-formats": "^2.1.1" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@rjsf/utils": "^6.10.1" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.9.tgz", + "integrity": "sha512-tNISae1QEf/vkb3xkRcjV5SEdzPE97We5IVaa2Z8jSszQPZ8U60B/YCYpw4QI7VidYsBtKavczXf+DyDs9WGxw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.9.tgz", + "integrity": "sha512-YC8YsI30o606GTZi0VyzYlsDKFP8W61i/QzayHDkLbNEz/IShqAmTa+hsJRj13xTHA0H+6fk4b2UmGn+Q/cMlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.9.tgz", + "integrity": "sha512-IwhlH3qK5urrY8hZiEgGkHKEFN901p/p2bjxCxJlr4GyNnF7wYpUvK+Y43uaRYuC4hpfjzbR3SJC3arX1jGvmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.9.tgz", + "integrity": "sha512-XxpJfVzFh+jilRxIXUqcfYAYcunIc/XEzIizsOL1fcJee5Sf7H3mH8WlLmfHfluz5amqR88QQo9izKtmMlavAw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.9.tgz", + "integrity": "sha512-kSfvhmgeWyfkbT3p/1s5vSgboogoah2zkm9fX2zjg2hHxSV7T4KhMWRUUaRk4OXNqoD3QAUeRqLcs1aZOK4U1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.9.tgz", + "integrity": "sha512-1RVzG17pxqbTfYLC352JlLt6kKLG+6Hr30n8DlIJqsnV5luUDd2Qdx9Ayw1Cabfyb1K9k0jXEZ7evxkRoT+uiw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.9.tgz", + "integrity": "sha512-BXqPvZ2drqVD+/Z8UpKwcs4Mp7grM+eGFku4CAEKrEtcbAsUpzREphK1sogCRZGreVPiMkiiBtw0n3TPteuqvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.9.tgz", + "integrity": "sha512-11vWvo8YDwLzukt27J3aYDWU+gg2P7J+ZOmiJ0hkF5BXZDW7pVya7r40MXDy6ya0i9KamoENSVKIugvJNgFXIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.9.tgz", + "integrity": "sha512-a1tijMkdwsIARtc0F39ApURROkf3NwqinI6TOiSSWCTR7dT96dffNvMUtDHnq64wKNTIZOIlzKrFvvFUznJiyw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.9.tgz", + "integrity": "sha512-x6SQNdAvv4c3hWqTMaWuawzMX9myaCs/yEmlGsxJzkdClnHW7FbrjQuSiRDhuSYzEYoEMhsaJy9qHG/XNemJPQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.9.tgz", + "integrity": "sha512-9s0AZ8BFK5/n7B/TBoa2yJE3gI3KURrbXcPBlsAsvjU4VeJKgE90y1YtNxyEUIcHPQkg6/yfF3qihUrcM/Kf0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.9.tgz", + "integrity": "sha512-P7VWAmV+WdJluH7ovnRGoiv2i8To7GAZ+kGzfGup635cyL7SyYl3lSUaA3Gp5THf0n/Co5EyEqb2zbqq+nMOHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.9.tgz", + "integrity": "sha512-1qixtsE4BK8h+yS3BfmZ09UhA7O/N4IACva6YBr7EBvCJraByTuRcgOTaiA62Tm0vey3UcKXLOaoGHtYmNGEVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.9.tgz", + "integrity": "sha512-ok8IQjcEPs1AKZfuEUznVBrJw+gK4soq+bx8b1X2XoMqVClarc1q5JDmVtWXY1xfr6ZuHTAsPXHTgTrqKTZeww==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.9.tgz", + "integrity": "sha512-Ip2mXoU0hM0boq3Rf+ekuT653OROSo6aSYcPT1VHE4q52KvyxgFkQgrgb/IEsxOuvQ2fZZbs8khJAyCEPM24/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@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/@swc/helpers": { + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.0.0" } @@ -2531,13 +3082,6 @@ "dev": true, "license": "MIT" }, - "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.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -2630,260 +3174,324 @@ "dev": true, "license": "MIT" }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.1.tgz", - "integrity": "sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.70.1", - "@typescript-eslint/type-utils": "8.70.1", - "@typescript-eslint/utils": "8.70.1", - "@typescript-eslint/visitor-keys": "8.70.1", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.70.1", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.9.tgz", - "integrity": "sha512-brTTsvFRt5C1gGHtPst/281UjPD5t9fBqbgoMPlVWy11ZLTPfu7HxK4ZYqO9H7o/yC9rSTCI85EaQ4OoY12qYw==", - "dev": true, - "license": "MIT", + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 4" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.70.1.tgz", - "integrity": "sha512-nO974WLllwhSFWQXnMLj6nDGa8f0khKEz1JzpPJ1u7Vm/4X1X6ZHajpoknU4bb41vJyMB0HHVyS2GqdhWfIXZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.70.1", - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/typescript-estree": "8.70.1", - "@typescript-eslint/visitor-keys": "8.70.1", - "debug": "^4.4.3" - }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.70.1.tgz", - "integrity": "sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.70.1", - "@typescript-eslint/types": "^8.70.1", - "debug": "^4.4.3" - }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.1.tgz", - "integrity": "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/visitor-keys": "8.70.1" - }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.1.tgz", - "integrity": "sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg==", - "dev": true, - "license": "MIT", + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.70.1.tgz", - "integrity": "sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/typescript-estree": "8.70.1", - "@typescript-eslint/utils": "8.70.1", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" - }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", - "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", - "dev": true, - "license": "MIT", + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.1.tgz", - "integrity": "sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.70.1", - "@typescript-eslint/tsconfig-utils": "8.70.1", - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/visitor-keys": "8.70.1", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.70.1.tgz", - "integrity": "sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.70.1", - "@typescript-eslint/types": "8.70.1", - "@typescript-eslint/typescript-estree": "8.70.1" - }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz", - "integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.70.1", - "eslint-visitor-keys": "^5.0.0" - }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/visitor-keys/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, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=16.20.0" } }, "node_modules/@visulima/boxen": { @@ -2911,37 +3519,6 @@ "node": ">=20.18 <=25.x" } }, - "node_modules/@vitest/eslint-plugin": { - "version": "1.6.27", - "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.6.27.tgz", - "integrity": "sha512-X1RCAfwbatG4GFbJ/1PIHP9MSZMt0JJThqbYID+vS4L783k6QGlLPe421wQE8dHXuGCcenVLsydiM4qzsYWxhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "^8.58.0", - "@typescript-eslint/utils": "^8.58.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "*", - "eslint": ">=8.57.0", - "typescript": ">=5.0.0", - "vitest": "*" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "typescript": { - "optional": true - }, - "vitest": { - "optional": true - } - } - }, "node_modules/@vitest/mocker": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.1.tgz", @@ -3951,29 +4528,6 @@ "integrity": "sha512-9P9fvFFxuiDcLqX0BYgGNkf0/a/id32nHvQpwgv/Xv3b9n5yeyw2U8nKefpWr+1VfFsrpf2XMXiA2CDSWZgt2g==", "license": "MIT" }, - "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/ag-charts-types": { "version": "14.2.0", "resolved": "https://registry.npmjs.org/ag-charts-types/-/ag-charts-types-14.2.0.tgz", @@ -4209,20 +4763,6 @@ "integrity": "sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==", "license": "MIT" }, - "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/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -4377,21 +4917,6 @@ "node": ">= 6" } }, - "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-tree": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", @@ -4480,13 +5005,6 @@ } } }, - "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", @@ -4669,191 +5187,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint": { - "version": "10.11.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.11.0.tgz", - "integrity": "sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ==", - "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.3", - "@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": "11.1.5 || >11.1.6 <12", - "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-config-prettier": { - "version": "10.1.8", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", - "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.7.tgz", - "integrity": "sha512-XhJSzLljuYD4UjNuFGJu2v7aD3sqNVK12w5/DCUfrNfHWegZo2+TsavNx8MuxMwEquuFAzNbNfXKU0WoZ+YUIg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "eslint": "^9 || ^10" - } - }, - "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": "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/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/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/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/espree/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/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -4868,42 +5201,6 @@ "node": ">=4" } }, - "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/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -4914,16 +5211,6 @@ "@types/estree": "^1.0.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/exit-hook": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-5.1.0.tgz", @@ -4986,33 +5273,6 @@ "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-string-truncated-width": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", @@ -5060,76 +5320,30 @@ "version": "1.20.3", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "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", - "dependencies": { - "flat-cache": "^6.1.23" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "license": "MIT" - }, - "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" + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" } }, - "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==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { - "cacheable": "^2.5.0", - "flatted": "^3.4.2", - "hookified": "^1.15.0" + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/flatted": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", - "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", - "dev": true, - "license": "ISC" + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" }, "node_modules/fsevents": { "version": "2.3.3", @@ -5179,29 +5393,16 @@ } }, "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==", + "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.3" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "17.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.12.0.tgz", - "integrity": "sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 6" } }, "node_modules/globby": { @@ -5225,29 +5426,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby/node_modules/ignore": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.9.tgz", - "integrity": "sha512-brTTsvFRt5C1gGHtPst/281UjPD5t9fBqbgoMPlVWy11ZLTPfu7HxK4ZYqO9H7o/yC9rSTCI85EaQ4OoY12qYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "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/hasown": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", @@ -5275,13 +5453,6 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, - "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", @@ -5376,9 +5547,9 @@ } }, "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.9.tgz", + "integrity": "sha512-brTTsvFRt5C1gGHtPst/281UjPD5t9fBqbgoMPlVWy11ZLTPfu7HxK4ZYqO9H7o/yC9rSTCI85EaQ4OoY12qYw==", "dev": true, "license": "MIT", "engines": { @@ -5401,16 +5572,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "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/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -5506,13 +5667,6 @@ "node": ">=18" } }, - "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/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5603,13 +5757,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "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/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -5632,30 +5779,6 @@ "node": ">=0.10.0" } }, - "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/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.33.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", @@ -5935,22 +6058,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "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": { "version": "4.18.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", @@ -6162,13 +6269,6 @@ "node": "^22 || ^24 || >=26" } }, - "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/next-themes": { "version": "0.4.6", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", @@ -6233,54 +6333,123 @@ "node": ">=12.20.0" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/oxfmt": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/oxfmt/-/oxfmt-0.63.0.tgz", + "integrity": "sha512-kgdDwv35wvVf6554U2Ab8Jnd0zTM+TsEQWwaB70RAjK3gICFAFGO+2Hd3Be27GMoXj3XRL9IKSNRVl7KBQL6iw==", "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" + "tinypool": "2.1.0" + }, + "bin": { + "oxfmt": "bin/oxfmt" }, "engines": { - "node": ">= 0.8.0" + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxfmt/binding-android-arm-eabi": "0.63.0", + "@oxfmt/binding-android-arm64": "0.63.0", + "@oxfmt/binding-darwin-arm64": "0.63.0", + "@oxfmt/binding-darwin-x64": "0.63.0", + "@oxfmt/binding-freebsd-x64": "0.63.0", + "@oxfmt/binding-linux-arm-gnueabihf": "0.63.0", + "@oxfmt/binding-linux-arm-musleabihf": "0.63.0", + "@oxfmt/binding-linux-arm64-gnu": "0.63.0", + "@oxfmt/binding-linux-arm64-musl": "0.63.0", + "@oxfmt/binding-linux-ppc64-gnu": "0.63.0", + "@oxfmt/binding-linux-riscv64-gnu": "0.63.0", + "@oxfmt/binding-linux-riscv64-musl": "0.63.0", + "@oxfmt/binding-linux-s390x-gnu": "0.63.0", + "@oxfmt/binding-linux-x64-gnu": "0.63.0", + "@oxfmt/binding-linux-x64-musl": "0.63.0", + "@oxfmt/binding-openharmony-arm64": "0.63.0", + "@oxfmt/binding-win32-arm64-msvc": "0.63.0", + "@oxfmt/binding-win32-ia32-msvc": "0.63.0", + "@oxfmt/binding-win32-x64-msvc": "0.63.0" + }, + "peerDependencies": { + "svelte": "^5.0.0", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "svelte": { + "optional": true + }, + "vite-plus": { + "optional": true + } } }, - "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==", + "node_modules/oxlint": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.85.0.tgz", + "integrity": "sha512-bc26s97nuvPj1ViyPsqmKecVkUWFMEdtayO8MaQ6oiLfs1pj94cQlZZhrh4BPNlr9HQosjhIlwgZKsfcwmcNgg==", "dev": true, "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" + "bin": { + "oxlint": "bin/oxlint" }, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/oxc-project" + }, + "optionalDependencies": { + "@oxlint/binding-android-arm-eabi": "1.85.0", + "@oxlint/binding-android-arm64": "1.85.0", + "@oxlint/binding-darwin-arm64": "1.85.0", + "@oxlint/binding-darwin-x64": "1.85.0", + "@oxlint/binding-freebsd-x64": "1.85.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.85.0", + "@oxlint/binding-linux-arm-musleabihf": "1.85.0", + "@oxlint/binding-linux-arm64-gnu": "1.85.0", + "@oxlint/binding-linux-arm64-musl": "1.85.0", + "@oxlint/binding-linux-ppc64-gnu": "1.85.0", + "@oxlint/binding-linux-riscv64-gnu": "1.85.0", + "@oxlint/binding-linux-riscv64-musl": "1.85.0", + "@oxlint/binding-linux-s390x-gnu": "1.85.0", + "@oxlint/binding-linux-x64-gnu": "1.85.0", + "@oxlint/binding-linux-x64-musl": "1.85.0", + "@oxlint/binding-openharmony-arm64": "1.85.0", + "@oxlint/binding-win32-arm64-msvc": "1.85.0", + "@oxlint/binding-win32-ia32-msvc": "1.85.0", + "@oxlint/binding-win32-x64-msvc": "1.85.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=7.0.2001", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + }, + "vite-plus": { + "optional": true + } } }, - "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==", + "node_modules/oxlint-tsgolint": { + "version": "7.0.2002", + "resolved": "https://registry.npmjs.org/oxlint-tsgolint/-/oxlint-tsgolint-7.0.2002.tgz", + "integrity": "sha512-rQAj2RAQM57nCwTd3PhtnAkREGW2StqI47tzudNlJF8WwGommM7rVpeP4O1O8/ZiuDZ9f2GnTESzLHGTluwkvQ==", "dev": true, "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" + "bin": { + "tsgolint": "bin/tsgolint.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optionalDependencies": { + "@oxlint-tsgolint/darwin-arm64": "7.0.2002", + "@oxlint-tsgolint/darwin-x64": "7.0.2002", + "@oxlint-tsgolint/linux-arm64": "7.0.2002", + "@oxlint-tsgolint/linux-x64": "7.0.2002", + "@oxlint-tsgolint/win32-arm64": "7.0.2002", + "@oxlint-tsgolint/win32-x64": "7.0.2002" } }, "node_modules/p-map": { @@ -6346,26 +6515,6 @@ "url": "https://github.com/inikulin/parse5?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/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -6473,20 +6622,10 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "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/prettier": { - "version": "3.9.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.8.tgz", - "integrity": "sha512-WRFq3Wn3WId7LLROfMLdH7xaFr2jR62wU8nLO6rQUOLOxNZUviyJQs1M0iIhLexSFy+L+w0ch66wtoO2jRjG0A==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "dev": true, "license": "MIT", "bin": { @@ -6547,26 +6686,6 @@ "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", @@ -6917,29 +7036,6 @@ "semver": "bin/semver.js" } }, - "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", @@ -7155,6 +7251,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinypool": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-2.1.0.tgz", + "integrity": "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.0.0 || >=22.0.0" + } + }, "node_modules/tldts": { "version": "7.4.14", "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.14.tgz", @@ -7214,74 +7320,45 @@ "node": ">=20" } }, - "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, - "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/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", "devOptional": true, "license": "Apache-2.0", "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.70.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.70.1.tgz", - "integrity": "sha512-AcWG7KDjZ2THNXsgwttMaGmzVi0VFRlFYfqFHYQRbDpF3owuYbuiL8c7UUrd2k8s3PoSfIQrWfrGXfcElrWLYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.70.1", - "@typescript-eslint/parser": "8.70.1", - "@typescript-eslint/typescript-estree": "8.70.1", - "@typescript-eslint/utils": "8.70.1" + "tsc": "bin/tsc" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=16.20.0" }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" } }, "node_modules/undici": { @@ -7351,16 +7428,6 @@ "integrity": "sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA==", "license": "MIT" }, - "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/use-isomorphic-layout-effect": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", @@ -7632,22 +7699,6 @@ "node": "^22.14.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", @@ -7665,16 +7716,6 @@ "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/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", @@ -7726,19 +7767,6 @@ "url": "https://github.com/sponsors/eemeli" } }, - "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" - } - }, "node_modules/zod": { "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", diff --git a/package.json b/package.json index f1eebefa..892f7133 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,22 @@ { "name": "hydrant", - "homepage": ".", "version": "0.1.0", "private": true, + "homepage": ".", "type": "module", + "scripts": { + "build": "react-router build", + "ci-all": "npm test && npm run lint && npm run format:check", + "dev": "react-router dev", + "format": "oxfmt", + "format:check": "oxfmt --check", + "lint": "oxlint", + "lint:fix": "oxlint --fix", + "postinstall": "chakra typegen src/components/ui/theme.ts && react-router typegen", + "preview": "vite preview", + "test": "vitest --run --reporter=verbose", + "test:watch": "vitest --reporter=verbose" + }, "dependencies": { "@chakra-ui/react": "^3.37.0", "@emotion/cache": "^11.14.0", @@ -33,21 +46,6 @@ "temporal-polyfill": "^1.0.5", "timezones-ical-library": "^2.3.2" }, - "scripts": { - "dev": "react-router dev", - "typecheck": "react-router typegen && tsc", - "build": "react-router build", - "preview": "vite preview", - "format": "prettier . --write", - "ci-prettier": "prettier . --check --ignore-unknown", - "ci-eslint": "eslint .", - "test": "vitest --run --reporter=verbose", - "test-watch": "vitest --reporter=verbose", - "ci-all": "npm run typecheck && npm test && npm run ci-eslint && npm run ci-prettier" - }, - "engines": { - "node": "^22.22.2 || >=24.15.0 || >=26.0.0" - }, "devDependencies": { "@chakra-ui/cli": "^3.37.0", "@eslint/js": "^10.0.1", @@ -57,18 +55,17 @@ "@types/node": "^22.20.1", "@types/react": "^19.3.0", "@types/react-dom": "19.3.0", - "@vitest/eslint-plugin": "^1.6.27", - "eslint": "^10.11.0", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-react-refresh": "^0.5.7", - "globals": "^17.12.0", "jsdom": "^30.1.0", - "prettier": "^3.9.8", - "typescript": "^6.0.3", - "typescript-eslint": "^8.70.1", + "oxfmt": "^0.63.0", + "oxlint": "^1.78.0", + "oxlint-tsgolint": "^7.0.2001", + "typescript": "^7.0.2", "vite": "^8.3.0", "vitest": "^5.0.1" }, + "engines": { + "node": "^22.22.2 || >=24.15.0 || >=26.0.0" + }, "allowScripts": { "esbuild@0.28.2": true, "msgpackr-extract@3.0.4": true diff --git a/pyproject.toml b/pyproject.toml index 60b72e6f..006a291c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,10 +7,10 @@ license = "MIT" license-files = ["LICENSE.txt"] requires-python = ">=3.8.2" dependencies = [ - "beautifulsoup4>=4.15.0", - "lxml>=6.1.2", - "tomli>=2.3.0; python_version < '3.11'", - "nltk>=3.9.1", + "beautifulsoup4>=4.15.0", + "lxml>=6.1.2", + "tomli>=2.3.0; python_version < '3.11'", + "nltk>=3.9.1", ] authors = [{ name = "SIPB Hydrant Team", email = "sipb-hydrant@mit.edu" }] diff --git a/scrapers/overrides.toml.d/override-schema.json b/scrapers/overrides.toml.d/override-schema.json index 5c2beed0..3c4875ba 100644 --- a/scrapers/overrides.toml.d/override-schema.json +++ b/scrapers/overrides.toml.d/override-schema.json @@ -73,7 +73,7 @@ "title": "Lecture sections", "description": "Lecture timeslots and rooms", "items": { - "$ref": "#/$defs/rawSection" + "$ref": "#/definitions/rawSection" } }, "recitationSections": { @@ -81,7 +81,7 @@ "title": "Recitation sections", "description": "Recitation timeslots and rooms", "items": { - "$ref": "#/$defs/rawSection" + "$ref": "#/definitions/rawSection" } }, "labSections": { @@ -89,7 +89,7 @@ "title": "Lab sections", "description": "Lab timeslots and rooms", "items": { - "$ref": "#/$defs/rawSection" + "$ref": "#/definitions/rawSection" } }, "designSections": { @@ -97,7 +97,7 @@ "title": "Design sections", "description": "Design timeslots and rooms", "items": { - "$ref": "#/$defs/rawSection" + "$ref": "#/definitions/rawSection" } }, "lectureRawSections": { @@ -430,11 +430,11 @@ "properties": { "start": { "title": "Start Date", - "$ref": "#/$defs/quarterDate" + "$ref": "#/definitions/quarterDate" }, "end": { "title": "End Date", - "$ref": "#/$defs/quarterDate" + "$ref": "#/definitions/quarterDate" } } }, @@ -625,7 +625,7 @@ } ] }, - "$defs": { + "definitions": { "rawTimeSlot": { "type": "array", "title": "Raw timeslot", @@ -652,7 +652,7 @@ { "type": "array", "items": { - "$ref": "#/$defs/rawTimeSlot" + "$ref": "#/definitions/rawTimeSlot" } }, { diff --git a/scrapers/overrides.toml.d/sem/21M.toml b/scrapers/overrides.toml.d/sem/21M.toml index 2b2dd974..adb22214 100644 --- a/scrapers/overrides.toml.d/sem/21M.toml +++ b/scrapers/overrides.toml.d/sem/21M.toml @@ -7,14 +7,8 @@ inCharge = "M. Rau, P. Smaragdis" ["21M.460"] sectionKinds = ["lecture"] -lectureRawSections = [ - "W18-1202/M/1/8.30-10 PM/T/1/7-8.30 PM", - "W18-1202/MR/1/7-8.30 PM", -] -lectureSections = [ - [[[29, 3], [60, 3]], "W18-1202"], - [[[26, 3], [128, 3]], "W18-1202"], -] +lectureRawSections = ["W18-1202/M/1/8.30-10 PM/T/1/7-8.30 PM", "W18-1202/MR/1/7-8.30 PM"] +lectureSections = [[[[29, 3], [60, 3]], "W18-1202"], [[[26, 3], [128, 3]], "W18-1202"]] ["21M.500"] description = "In this seminar we will be taking a global-historical approach to the “baroque,” decentering our understanding of its associated music-makings in the 16th to 18th centuries as well as its postcolonial continuities in our present time. By adopting a polycentric perspective, we will study the history of what is often referred to as “Western music” and its relationship to European colonialism, trade, and missionizing in this period. These global networks of power will be explored through a series of connected vignettes ranging from the early modern imperial courts and trading ports of South, Southeast, and East Asia, to the reducciones of South America and beyond. A focus on surviving repertoire from these regions will encourage you to think not only about a more diverse range of music-makers in history, but to also question the “Europeanness” of these musical traditions both past and present, taking seriously Indigenous contributions to and ownership of these musics. " diff --git a/src/components/ActivityButtons.tsx b/src/components/ActivityButtons.tsx index c0cdc27f..aeae64e5 100644 --- a/src/components/ActivityButtons.tsx +++ b/src/components/ActivityButtons.tsx @@ -1,4 +1,10 @@ -import { useLayoutEffect, useState, type SubmitEventHandler } from "react"; +import { + useLayoutEffect, + useState, + type Dispatch, + type SetStateAction, + type SubmitEventHandler, +} from "react"; import { Button, @@ -17,11 +23,10 @@ import { parseColor, type ButtonProps, } from "@chakra-ui/react"; -import { ColorPickerInput } from "./ui/colorpicker-input"; import { LuCheck as CheckIcon, LuX as CloseIcon } from "react-icons/lu"; -import { Checkbox } from "./ui/checkbox"; -import { Field } from "./ui/field"; -import { Radio, RadioGroup } from "./ui/radio"; + +import type { Class } from "../lib/class"; +import type { PEClass } from "../lib/pe"; import { Timeslot, @@ -31,11 +36,13 @@ import { type Sections, type SectionLockOption, } from "../lib/activity"; -import type { Class } from "../lib/class"; -import type { PEClass } from "../lib/pe"; -import { PESection } from "../lib/pe"; import { Slot, TIMESLOT_STRINGS, WEEKDAY_STRINGS } from "../lib/dates"; import { useHydrantContext } from "../lib/hydrant"; +import { PESection } from "../lib/pe"; +import { Checkbox } from "./ui/checkbox"; +import { ColorPickerInput } from "./ui/colorpicker-input"; +import { Field } from "./ui/field"; +import { Radio, RadioGroup } from "./ui/radio"; interface ToggleButtonProps extends ButtonProps { active: boolean; @@ -137,70 +144,76 @@ const getLabel = (sec: SectionLockOption, humanReadable?: boolean) => { } }; +const RenderOptions = (props: { + cls: Class | PEClass; + selected: string[]; + setSelected: Dispatch>; +}) => { + const { state } = useHydrantContext(); + const { cls, selected, setSelected } = props; + + return ( + <> + {cls.sections.map((secs, sectionIndex) => { + const options = [LockOption.Auto, LockOption.None, ...secs.sections]; + return ( + + { + setSelected((oldArray) => { + oldArray[sectionIndex] = e.value ?? ""; + return oldArray; + }); + + if (e.value === LockOption.Auto) { + state.lockSection(secs, LockOption.Auto); + return; + } + + if (e.value === LockOption.None) { + state.lockSection(secs, LockOption.None); + return; + } + + const foundSec = secs.sections.find( + (sec) => sec.rawTime === e.value, + ); + + if (foundSec) { + state.lockSection(secs, foundSec); + } + }} + > + + {options.map((sec) => ( + + {getLabel(sec, true)} + + ))} + + + + + ); + })} + + ); +}; + /** Div containing section manual selection interface. */ function ClassManualSections(props: { cls: Class | PEClass }) { const { cls } = props; - const { state } = useHydrantContext(); const [selected, setSelected] = useState(genSelected(cls)); useLayoutEffect(() => { setSelected(genSelected(cls)); }, [cls]); - const RenderOptions = () => { - return ( - <> - {cls.sections.map((secs, sectionIndex) => { - const options = [LockOption.Auto, LockOption.None, ...secs.sections]; - return ( - - { - setSelected((oldArray) => { - oldArray[sectionIndex] = e.value ?? ""; - return oldArray; - }); - - if (e.value === LockOption.Auto) { - state.lockSection(secs, LockOption.Auto); - return; - } - - if (e.value === LockOption.None) { - state.lockSection(secs, LockOption.None); - return; - } - - const foundSec = secs.sections.find( - (sec) => sec.rawTime === e.value, - ); - - if (foundSec) { - state.lockSection(secs, foundSec); - } - }} - > - - {options.map((sec) => ( - - {getLabel(sec, true)} - - ))} - - - - - ); - })} - - ); - }; - return ( - + ); } @@ -258,6 +271,28 @@ function ActivityColor(props: { activity: Activity; onHide: () => void }) { ); } +const RenderCheckboxes = (props: { + days: Record; + setDays: Dispatch>>; +}) => { + const { days, setDays } = props; + return ( + <> + {WEEKDAY_STRINGS.map((day) => ( + { + setDays({ ...days, [day]: !!e.checked }); + }} + > + {day} + + ))} + + ); +}; + /** Buttons in class description to add/remove class, and lock sections. */ export function ClassButtons(props: { cls: Class | PEClass }) { const { cls } = props; @@ -340,24 +375,6 @@ function CustomActivityAddTime(props: { activity: CustomActivity }) { } }; - const RenderCheckboxes = () => { - return ( - <> - {WEEKDAY_STRINGS.map((day) => ( - { - setDays({ ...days, [day]: !!e.checked }); - }} - > - {day} - - ))} - - ); - }; - const renderTimeDropdown = (key: "start" | "end") => ( - + {renderTimeDropdown("start")} to {renderTimeDropdown("end")} @@ -409,6 +426,147 @@ function CustomActivityAddTime(props: { activity: CustomActivity }) { ); } +const RenderHeading = (props: { + isRenaming: boolean; + isRelocating: boolean; + activity: CustomActivity; + name: string; + room: string | undefined; + setName: Dispatch>; + setRoom: Dispatch>; + onConfirmRename: () => void; + onCancelRename: () => void; + onConfirmRelocating: () => void; + onCancelRelocating: () => void; +}) => { + const { + isRenaming, + isRelocating, + activity, + name, + room, + setName, + setRoom, + onConfirmRename, + onCancelRename, + onConfirmRelocating, + onCancelRelocating, + } = props; + + if (isRenaming) { + return ( + { + setName(e.target.value); + }} + fontWeight="bold" + placeholder="New Activity" + autoFocus + onKeyUp={(e) => { + if (e.key === "Enter") { + onConfirmRename(); + } else if (e.key === "Escape") { + onCancelRename(); + } + }} + /> + ); + } else if (isRelocating) { + return ( + { + setRoom(e.target.value); + }} + placeholder="W20-557" + autoFocus + onKeyUp={(e) => { + if (e.key === "Enter") { + onConfirmRelocating(); + } else if (e.key === "Escape") { + onCancelRelocating(); + } + }} + /> + ); + } else { + return {activity.name}; + } +}; + +const RenderButtons = (props: { + isRenaming: boolean; + isRelocating: boolean; + activity: CustomActivity; + isSelected: boolean; + showColors: boolean; + setShowColors: Dispatch>; + onRenameElse: () => void; + onRelocateElse: () => void; + onConfirmRename: () => void; + onCancelRename: () => void; + onConfirmRelocating: () => void; + onCancelRelocating: () => void; +}) => { + const { + isRenaming, + isRelocating, + activity, + isSelected, + showColors, + setShowColors, + onRenameElse, + onRelocateElse, + onConfirmRename, + onCancelRename, + onConfirmRelocating, + onCancelRelocating, + } = props; + const { state } = useHydrantContext(); + + if (isRenaming) { + return ( + <> + + + + ); + } else if (isRelocating) { + return ( + <> + + + + ); + } else { + return ( + <> + + + + {isSelected && ( + { + setShowColors(!showColors); + }} + > + Edit color + + )} + + ); + } +}; + /** * Buttons in custom activity description to rename it, or add/edit/remove timeslots. */ @@ -424,49 +582,6 @@ export function CustomActivityButtons(props: { activity: CustomActivity }) { const [name, setName] = useState(activity.name); const [room, setRoom] = useState(activity.room); - const RenderHeading = () => { - if (isRenaming) { - return ( - { - setName(e.target.value); - }} - fontWeight="bold" - placeholder="New Activity" - autoFocus - onKeyUp={(e) => { - if (e.key === "Enter") { - onConfirmRename(); - } else if (e.key === "Escape") { - onCancelRename(); - } - }} - /> - ); - } else if (isRelocating) { - return ( - { - setRoom(e.target.value); - }} - placeholder="W20-557" - autoFocus - onKeyUp={(e) => { - if (e.key === "Enter") { - onConfirmRelocating(); - } else if (e.key === "Escape") { - onCancelRelocating(); - } - }} - /> - ); - } else { - return {activity.name}; - } - }; - const onConfirmRename = () => { state.renameCustomActivity(activity, name); setIsRenaming(false); @@ -491,53 +606,37 @@ export function CustomActivityButtons(props: { activity: CustomActivity }) { setIsRelocating(true); }; - const RenderButtons = () => { - if (isRenaming) { - return ( - <> - - - - ); - } else if (isRelocating) { - return ( - <> - - - - ); - } else { - return ( - <> - - - - {isSelected && ( - { - setShowColors(!showColors); - }} - > - Edit color - - )} - - ); - } - }; - return ( - {RenderHeading()} - {RenderButtons()} + + + + {isSelected && showColors && ( { + const { smallText, titleClass, title } = props; + return ( + + {title} + + ); +}; + +const RoomText = (props: { + smallText: boolean; + timeClass: string; + room?: string; +}) => { + const { smallText, timeClass, room } = props; + return ( + + {room} + + ); +}; + /** * Calendar showing all the activities, including the buttons on top that * change the schedule option selected. @@ -83,11 +111,17 @@ export function Calendar() { * return undefined. */ const getDistanceWarning = (thisEvent: EventApi) => { - const thisRoom = thisEvent.extendedProps.room as string | undefined; + const thisRoom = thisEvent.extendedProps.room; if (!thisEvent.start || !thisRoom) { return undefined; } + if (typeof thisRoom !== "string" && thisRoom !== undefined) { + return undefined; + } + + thisRoom satisfies string | undefined; + for (const beforeEvent of events) { if (!beforeEvent.start || !beforeEvent.room) { continue; @@ -135,26 +169,22 @@ export function Calendar() { isNarrow, isShort, }: EventDisplayInfo) => { - const room = event.extendedProps.room as string | undefined; - const activity = event.extendedProps.activity as Activity; + // ensure event is activity + if (!isActivity(event.extendedProps.activity)) { + return null; + } + + const room = event.extendedProps.room; + const activity = event.extendedProps.activity; const distanceWarning = getDistanceWarning(event); const smallText = isNarrow || isShort; - const TitleText = () => ( - - {event.title} - - ); + // ensure room is either string or undefined + if (typeof room !== "string" && room !== undefined) { + return null; + } - const RoomText = () => ( - - {room} - - ); + room satisfies string | undefined; return ( <> @@ -164,21 +194,29 @@ export function Calendar() { portalled positioning={{ placement: "top" }} > - {TitleText()} + {TitleText({ + smallText, + titleClass, + title: event.title, + })} ) : ( - + )} {event.extendedProps.roomClarification ? ( - {RoomText()} + {RoomText({ smallText, timeClass, room })} ) : ( - + )} {distanceWarning ? ( @@ -219,7 +257,9 @@ export function Calendar() { eventContent={renderEvent} eventClick={(e) => { // extendedProps: non-standard props of {@link Event.eventInputs} - state.setViewedActivity(e.event.extendedProps.activity as Activity); + if (isActivity(e.event.extendedProps.activity)) { + state.setViewedActivity(e.event.extendedProps.activity); + } }} headerToolbar={false} height="auto" diff --git a/src/components/ClassTable.tsx b/src/components/ClassTable.tsx index beb80f9a..a0664f9b 100644 --- a/src/components/ClassTable.tsx +++ b/src/components/ClassTable.tsx @@ -9,7 +9,17 @@ import { type SetStateAction, } from "react"; -import { AgGridReact } from "ag-grid-react"; +import { + Box, + Flex, + Image, + Input, + Button, + ButtonGroup, + InputGroup, + CloseButton, + Span, +} from "@chakra-ui/react"; import { ModuleRegistry, ClientSideRowModelModule, @@ -23,30 +33,20 @@ import { type ColDef, type Module, } from "ag-grid-community"; - -import { - Box, - Span, - Flex, - Image, - Input, - Button, - ButtonGroup, - InputGroup, - CloseButton, -} from "@chakra-ui/react"; -import { LuPlus, LuMinus, LuSearch, LuBadgePlus, LuStar } from "react-icons/lu"; -import { LabelledButton } from "./ui/button"; -import { useColorModeValue } from "./ui/color-mode"; -import { Tooltip } from "./ui/tooltip"; +import { AgGridReact, type CustomCellRendererProps } from "ag-grid-react"; +import { LuPlus, LuMinus, LuSearch, LuStar, LuBadgePlus } from "react-icons/lu"; import type { Class, Flags } from "../lib/class"; -import { DARK_IMAGES, getFlagImg } from "../lib/class"; -import { classNumberMatch, classSort, simplifyString } from "../lib/utils"; import type { TSemester } from "../lib/dates"; -import { useHydrantContext } from "../lib/hydrant"; import type { State } from "../lib/state"; + +import { DARK_IMAGES, getFlagImg } from "../lib/class"; import { ColorStyles } from "../lib/colors"; +import { useHydrantContext } from "../lib/hydrant"; +import { classNumberMatch, classSort, simplifyString } from "../lib/utils"; +import { LabelledButton } from "./ui/button"; +import { useColorModeValue } from "./ui/color-mode"; +import { Tooltip } from "./ui/tooltip"; import styles from "./ClassTable.module.css"; @@ -266,8 +266,8 @@ function ClassInput(props: { onClassInputChange(""); } else if (state.classes.has(classInput)) { // else check if this number exists exactly - const cls = state.classes.get(classInput); - state.toggleActivity(cls); + const clsCheck = state.classes.get(classInput); + state.toggleActivity(clsCheck); } }; @@ -281,6 +281,23 @@ function ClassInput(props: { ); } +const StarCellRenderer = (props: CustomCellRendererProps) => { + const { data, api } = props; + if (!data) return null; + + return ( + { + api.refreshCells({ + force: true, + columns: ["number"], + }); + }} + /> + ); +}; + const filtersNonFlags = { fits: (state, cls) => state.fitsSchedule(cls), starred: (state, cls) => state.isClassStarred(cls), @@ -292,7 +309,7 @@ type FilterGroup = [Filter, string, ReactNode?][]; /** List of top filter IDs and their displayed names. */ const CLASS_FLAGS_1: FilterGroup = [ - ["starred", "Starred", ], + ["starred", "Starred", ], ["hass", "HASS"], ["cih", "CI-H"], ["cim", "CI-M"], @@ -342,6 +359,10 @@ const CLASS_FLAGS = [ ...CLASS_FLAGS_4, ]; +function isFilterNonFlagKey(key: string): key is keyof typeof filtersNonFlags { + return key in filtersNonFlags; +} + /** Div containing all the flags like "HASS". Maintains the flag filter. */ function ClassFlags(props: { /** Callback for updating the class filter. */ @@ -354,9 +375,9 @@ function ClassFlags(props: { // Map from flag to whether it's on. const [flags, setFlags] = useState>(() => { - const result = new Map(); + const result = new Map(); for (const flag of CLASS_FLAGS) { - result.set(flag, false); + result.set(flag[0], false); } return result; }); @@ -384,17 +405,17 @@ function ClassFlags(props: { setFlagsFilter(() => (cls?: Class) => { if (!cls) return false; let result = true; - newFlags.forEach((value, flag) => { + newFlags.forEach((flagVal, flagKey) => { if ( - value && - flag in filtersNonFlags && - !filtersNonFlags[flag as keyof typeof filtersNonFlags](state, cls) + flagVal && + isFilterNonFlagKey(flagKey) && + !filtersNonFlags[flagKey](state, cls) ) { result = false; } else if ( - value && - !(flag in filtersNonFlags) && - !cls.flags[flag as keyof typeof cls.flags] + flagVal && + !isFilterNonFlagKey(flagKey) && + !cls.flags[flagKey] ) { result = false; } @@ -404,8 +425,9 @@ function ClassFlags(props: { }; const filter = useColorModeValue( - (_flags: keyof Flags) => "", - (flag: keyof Flags) => (DARK_IMAGES.includes(flag) ? "invert()" : ""), + (_flags: Filter) => "", + (flag: Filter) => + !isFilterNonFlagKey(flag) && DARK_IMAGES.includes(flag) ? "invert()" : "", ); const renderGroup = (group: FilterGroup) => { @@ -434,11 +456,7 @@ function ClassFlags(props: { title={label} variant={checked ? "solid" : "outline"} > - {label} + {label} ) : ( // image is a react element, like an icon @@ -552,18 +570,7 @@ export function ClassTable() { headerName: "", field: "number", maxWidth: 49, - cellRenderer: (params: { value: string; data: ClassTableRow }) => ( - { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - gridRef.current?.api?.refreshCells({ - force: true, - columns: ["number"], - }); - }} - /> - ), + cellRenderer: StarCellRenderer, sortable: false, cellStyle: { padding: 0 }, }, @@ -649,7 +656,6 @@ export function ClassTable() { // Need to notify grid every time we update the filter useEffect(() => { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition gridRef.current?.api?.onFilterChanged(); }, [doesExternalFilterPass]); @@ -658,7 +664,6 @@ export function ClassTable() { gridRef.current?.api?.onFilterChanged()} /> diff --git a/src/components/ClassTypes.tsx b/src/components/ClassTypes.tsx index adf97b7f..a228143b 100644 --- a/src/components/ClassTypes.tsx +++ b/src/components/ClassTypes.tsx @@ -1,37 +1,34 @@ import { useState } from "react"; +import type { IconType } from "react-icons/lib"; + import { Tabs } from "@chakra-ui/react"; import { LuGraduationCap, LuDumbbell } from "react-icons/lu"; -import type { IconType } from "react-icons/lib"; +import { useHydrantContext } from "../lib/hydrant"; +import { ClassType } from "../lib/schema"; import { ClassTable } from "./ClassTable"; import { PEClassTable } from "./PEClassTable"; -import { ClassType } from "../lib/schema"; -import { useHydrantContext } from "../lib/hydrant"; - -function classTypeComponents(termKeys: ClassType[]) { - const obj = {} as Record; - - if (termKeys.includes(ClassType.ACADEMIC)) { - obj[ClassType.ACADEMIC] = [LuGraduationCap, ClassTable]; - } +type ClassTypeComponent = Record; - if (termKeys.includes(ClassType.PEW)) { - obj[ClassType.PEW] = [LuDumbbell, PEClassTable]; - } - - return obj; -} +const CLASS_TYPE_COMPONENTS: ClassTypeComponent = { + [ClassType.ACADEMIC]: [LuGraduationCap, ClassTable], + [ClassType.PEW]: [LuDumbbell, PEClassTable], +}; export const ClassTypesSwitcher = () => { const { state } = useHydrantContext(); - const [currentTab, setCurrentTab] = useState(ClassType.ACADEMIC); + const [currentTab, setCurrentTab] = useState(ClassType.ACADEMIC); - const tabs = classTypeComponents([ - ...(state.classes.size > 0 ? [ClassType.ACADEMIC] : []), - ...(state.peClasses.size > 0 ? [ClassType.PEW] : []), - ]); + const tabs: Partial = { + ...(state.classes.size > 0 + ? { [ClassType.ACADEMIC]: CLASS_TYPE_COMPONENTS[ClassType.ACADEMIC] } + : {}), + ...(state.peClasses.size > 0 + ? { [ClassType.PEW]: CLASS_TYPE_COMPONENTS[ClassType.PEW] } + : {}), + }; if (Object.keys(tabs).length > 1) return ( @@ -41,12 +38,12 @@ export const ClassTypesSwitcher = () => { variant="line" value={currentTab} onValueChange={(e) => { - setCurrentTab(e.value as ClassType); + setCurrentTab(e.value); }} > {Object.entries(tabs).map(([key, [Icon, _]]) => ( - + {key} @@ -54,7 +51,7 @@ export const ClassTypesSwitcher = () => { {Object.entries(tabs).map(([key, [_, Component]]) => ( - + ))} diff --git a/src/components/Footers.tsx b/src/components/Footers.tsx index 836ae1fc..e726d5b5 100644 --- a/src/components/Footers.tsx +++ b/src/components/Footers.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { Link as RouterLink } from "react-router"; import { Flex, @@ -9,11 +10,9 @@ import { Dialog, Portal, } from "@chakra-ui/react"; -import { Link as RouterLink } from "react-router"; - -import { useHydrantContext } from "../lib/hydrant"; import fuzzAndAnt from "../assets/fuzzAndAnt.png"; +import { useHydrantContext } from "../lib/hydrant"; function AboutDialog() { const [visible, setVisible] = useState(false); diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 8848db16..89de425e 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -13,20 +13,20 @@ import { Select, Portal, } from "@chakra-ui/react"; -import { useColorModeValue } from "./ui/color-mode"; -import { LuSettings, LuX } from "react-icons/lu"; import { keyframes } from "@emotion/react"; +import { LuSettings, LuX } from "react-icons/lu"; -import { COLOR_SCHEME_PRESETS } from "../lib/colors"; -import { MEASUREMENT_SYSTEM_PRESETS } from "../lib/measurement"; import type { Preferences } from "../lib/schema"; -import { DEFAULT_PREFERENCES } from "../lib/schema"; -import { useHydrantContext } from "../lib/hydrant"; -import logo from "../assets/logo.svg"; -import logoDark from "../assets/logo-dark.svg"; import hydraAnt from "../assets/hydraAnt.png"; +import logoDark from "../assets/logo-dark.svg"; +import logo from "../assets/logo.svg"; +import { COLOR_SCHEME_PRESETS } from "../lib/colors"; +import { useHydrantContext } from "../lib/hydrant"; +import { MEASUREMENT_SYSTEM_PRESETS } from "../lib/measurement"; +import { DEFAULT_PREFERENCES } from "../lib/schema"; import { SIPBLogo } from "./ButtonsLinks"; +import { useColorModeValue } from "./ui/color-mode"; // Brief nudge when someone clicks outside the dialog, to hint that the click // was ignored rather than just doing nothing. Skipped under reduced motion. @@ -244,9 +244,9 @@ export function Header() { const [show, setShow] = useState(urlNameOrig !== null); const onClose = () => { - setSearchParams((searchParams) => { - searchParams.delete("ti"); - return searchParams; + setSearchParams((searchParamsNew) => { + searchParamsNew.delete("ti"); + return searchParamsNew; }); setShow(false); }; diff --git a/src/components/PEClassTable.tsx b/src/components/PEClassTable.tsx index 7d983a27..30917e17 100644 --- a/src/components/PEClassTable.tsx +++ b/src/components/PEClassTable.tsx @@ -8,29 +8,38 @@ import { type SetStateAction, } from "react"; -import { AgGridReact } from "ag-grid-react"; import type { IRowNode, ColDef } from "ag-grid-community"; import { Box, Flex, Button, ButtonGroup } from "@chakra-ui/react"; +import { type CustomCellRendererProps } from "ag-grid-react"; +import { AgGridReact } from "ag-grid-react"; import { LuStar } from "react-icons/lu"; -import { LabelledButton } from "./ui/button"; +import type { State } from "../lib/state"; + +import { ColorStyles } from "../lib/colors"; +import { useHydrantContext } from "../lib/hydrant"; +import { type PEFlags, type PEClass, getPEFlagIcon } from "../lib/pe"; +import { classNumberMatch, classSort, simplifyString } from "../lib/utils"; import { HYDRANT_THEME, INITIAL_SORT, sortProps, ClassSearchInput, } from "./ClassTable"; - -import { type PEFlags, type PEClass, getPEFlagIcon } from "../lib/pe"; -import { classNumberMatch, classSort, simplifyString } from "../lib/utils"; -import { useHydrantContext } from "../lib/hydrant"; -import type { State } from "../lib/state"; -import { ColorStyles } from "../lib/colors"; +import { LabelledButton } from "./ui/button"; import styles from "./ClassTable.module.css"; -const getFeeColor = (fee: number) => { +const getFeeColor = (fee?: string | number | null) => { + if (fee === null || fee === undefined) return ColorStyles.Muted; + + if (typeof fee === "string") { + const num = Number(fee); + if (isNaN(num)) return ColorStyles.Muted; + fee = num; + } + if (isNaN(fee)) return ColorStyles.Muted; if (fee == 0) return ColorStyles.Success; if (fee <= 20) return ColorStyles.Warning; @@ -116,8 +125,8 @@ function ClassInput(props: { onClassInputChange(""); } else if (state.peClasses.has(classInput)) { // else check if this number exists exactly - const cls = state.peClasses.get(classInput); - state.toggleActivity(cls); + const clsCheck = state.peClasses.get(classInput); + state.toggleActivity(clsCheck); } }; @@ -131,6 +140,23 @@ function ClassInput(props: { ); } +const StarCellRenderer = (props: CustomCellRendererProps) => { + const { data, api } = props; + if (!data) return null; + + return ( + { + api.refreshCells({ + force: true, + columns: ["number"], + }); + }} + /> + ); +}; + const filtersNonFlags = { fits: (state, cls) => state.fitsSchedule(cls), starred: (state, cls) => state.isPEClassStarred(cls), @@ -144,7 +170,7 @@ type FilterGroup = [Filter, string, ReactNode?][]; * List of all class flags. */ const CLASS_FLAGS: FilterGroup = [ - ["starred", "Starred", ], + ["starred", "Starred", ], ["latest", "Latest quarter"], ["nofee", "No fee"], ["nopreq", "No prereq"], @@ -155,6 +181,10 @@ const CLASS_FLAGS: FilterGroup = [ ["remote", "Remote", getPEFlagIcon("remote")], ]; +function isFilterNonFlagKey(key: string): key is keyof typeof filtersNonFlags { + return key in filtersNonFlags; +} + /** Div containing all the flags like "HASS". Maintains the flag filter. */ function ClassFlags(props: { /** Callback for updating the class filter. */ @@ -167,9 +197,9 @@ function ClassFlags(props: { // Map from flag to whether it's on. const [flags, setFlags] = useState>(() => { - const result = new Map(); + const result = new Map(); for (const flag of CLASS_FLAGS) { - result.set(flag, false); + result.set(flag[0], false); } return result; }); @@ -194,17 +224,17 @@ function ClassFlags(props: { setFlagsFilter(() => (cls?: PEClass) => { if (!cls) return false; let result = true; - newFlags.forEach((value, flag) => { + newFlags.forEach((flagVal, flagKey) => { if ( - value && - flag in filtersNonFlags && - !filtersNonFlags[flag as keyof typeof filtersNonFlags](state, cls) + flagVal && + isFilterNonFlagKey(flagKey) && + !filtersNonFlags[flagKey](state, cls) ) { result = false; } else if ( - value && - !(flag in filtersNonFlags) && - !cls.flags[flag as keyof typeof cls.flags] + flagVal && + !isFilterNonFlagKey(flagKey) && + !cls.flags[flagKey] ) { result = false; } @@ -303,18 +333,7 @@ export function PEClassTable() { headerName: "", field: "number", maxWidth: 49, - cellRenderer: (params: { data: ClassTableRow }) => ( - { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - gridRef.current?.api?.refreshCells({ - force: true, - columns: ["number"], - }); - }} - /> - ), + cellRenderer: StarCellRenderer, sortable: false, cellStyle: { padding: 0 }, }, @@ -341,11 +360,8 @@ export function PEClassTable() { field: "fee", maxWidth: 90, cellDataType: "number", - cellClass: (params) => [ - getFeeColor(params.value as number), - styles.data, - ], - valueFormatter: (params) => "$" + (params.value as number).toFixed(2), + cellClass: (params) => getFeeColor(params.value), + valueFormatter: (params) => "$" + Number(params.value).toFixed(2), ...sortProps, }, { @@ -354,7 +370,7 @@ export function PEClassTable() { flex: 1, }, ]; - }, [state]); + }, []); const defaultColDef: ColDef = useMemo(() => { return { @@ -388,7 +404,6 @@ export function PEClassTable() { // Need to notify grid every time we update the filter useEffect(() => { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition gridRef.current?.api?.onFilterChanged(); }, [doesExternalFilterPass]); @@ -397,7 +412,6 @@ export function PEClassTable() { gridRef.current?.api?.onFilterChanged()} /> diff --git a/src/components/ScheduleSwitcher.tsx b/src/components/ScheduleSwitcher.tsx index bb0b49d1..d8bfdee6 100644 --- a/src/components/ScheduleSwitcher.tsx +++ b/src/components/ScheduleSwitcher.tsx @@ -28,6 +28,7 @@ import { } from "react-icons/lu"; import type { Save } from "../lib/schema"; + import { useHydrantContext } from "../lib/hydrant"; function SmallButton(props: ButtonProps) { @@ -254,7 +255,7 @@ export function ScheduleSwitcher() { const [renderHeading, renderButtons] = (() => { if (isRenaming) { - const renderHeading = () => ( + const renderHeadingRenaming = () => ( { @@ -281,16 +282,16 @@ export function ScheduleSwitcher() { setName(currentName); setIsRenaming(false); }; - const renderButtons = () => ( + const renderButtonsRenaming = () => ( <> Confirm Cancel ); - return [renderHeading, renderButtons]; + return [renderHeadingRenaming, renderButtonsRenaming]; } - const renderHeading = () => ( + const renderHeadingDefault = () => ( ); const onRename = () => { @@ -302,7 +303,7 @@ export function ScheduleSwitcher() { const onCopy = () => { state.addSave(false, `${currentName} copy`); }; - const renderButtons = () => ( + const renderButtonsDefault = () => ( @@ -382,7 +383,7 @@ export function ScheduleSwitcher() { ); - return [renderHeading, renderButtons]; + return [renderHeadingDefault, renderButtonsDefault]; })(); return ( diff --git a/src/components/SelectedActivities.tsx b/src/components/SelectedActivities.tsx index ba571574..1980bdff 100644 --- a/src/components/SelectedActivities.tsx +++ b/src/components/SelectedActivities.tsx @@ -8,8 +8,9 @@ import { import { LuPlus } from "react-icons/lu"; import type { Activity } from "../lib/activity"; -import { textColor } from "../lib/colors"; + import { Class } from "../lib/class"; +import { textColor } from "../lib/colors"; import { useHydrantContext } from "../lib/hydrant"; export function ColorButton(props: ButtonProps & { color: string }) { diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx index 131ca6a5..2a8faded 100644 --- a/src/components/ui/alert.tsx +++ b/src/components/ui/alert.tsx @@ -1,6 +1,7 @@ -import { Alert as ChakraAlert } from "@chakra-ui/react"; import { forwardRef, type ReactNode, type ReactElement } from "react"; +import { Alert as ChakraAlert } from "@chakra-ui/react"; + export interface AlertProps extends Omit { startElement?: ReactNode; endElement?: ReactNode; @@ -13,7 +14,6 @@ export const Alert = forwardRef( const { title, children, icon, startElement, endElement, ...rest } = props; return ( - {/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing */} {startElement || {icon}} {children ? ( diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index f427a650..59d1ce48 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,7 +1,10 @@ +import type { RefObject } from "react"; + import type { Tooltip as ChakraTooltip } from "@chakra-ui/react"; + import { Button, type ButtonProps } from "@chakra-ui/react"; + import { Tooltip } from "./tooltip"; -import type { RefObject } from "react"; export interface LabelledButtonProps extends ButtonProps { showArrow?: boolean; diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx index fb48e822..f545fc02 100644 --- a/src/components/ui/checkbox.tsx +++ b/src/components/ui/checkbox.tsx @@ -1,4 +1,3 @@ -import { Checkbox as ChakraCheckbox } from "@chakra-ui/react"; import { type ReactNode, type InputHTMLAttributes, @@ -6,6 +5,8 @@ import { forwardRef, } from "react"; +import { Checkbox as ChakraCheckbox } from "@chakra-ui/react"; + export interface CheckboxProps extends ChakraCheckbox.RootProps { icon?: ReactNode; inputProps?: InputHTMLAttributes; @@ -19,7 +20,6 @@ export const Checkbox = forwardRef( - {/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing */} {icon || } {children != null && ( diff --git a/src/components/ui/color-mode.tsx b/src/components/ui/color-mode.tsx index b1789165..bc07285d 100644 --- a/src/components/ui/color-mode.tsx +++ b/src/components/ui/color-mode.tsx @@ -1,10 +1,12 @@ "use client"; +import { forwardRef } from "react"; + import type { IconButtonProps, SpanProps } from "@chakra-ui/react"; +import type { ThemeProviderProps } from "next-themes"; + import { ClientOnly, IconButton, Skeleton, Span } from "@chakra-ui/react"; import { ThemeProvider, useTheme } from "next-themes"; -import type { ThemeProviderProps } from "next-themes"; -import { forwardRef } from "react"; import { LuMoon, LuSun } from "react-icons/lu"; export type ColorModeProviderProps = ThemeProviderProps; @@ -23,10 +25,8 @@ export interface UseColorModeReturn { toggleColorMode: () => void; } -// eslint-disable-next-line react-refresh/only-export-components export function useColorMode(): UseColorModeReturn { const { resolvedTheme, setTheme, forcedTheme } = useTheme(); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const colorMode = forcedTheme || resolvedTheme; const toggleColorMode = () => { setTheme(resolvedTheme === "dark" ? "light" : "dark"); @@ -38,7 +38,6 @@ export function useColorMode(): UseColorModeReturn { }; } -// eslint-disable-next-line react-refresh/only-export-components export function useColorModeValue(light: T, dark: T) { const { colorMode } = useColorMode(); return colorMode === "dark" ? dark : light; diff --git a/src/components/ui/colorpicker-input.tsx b/src/components/ui/colorpicker-input.tsx index a9ef209d..fa546841 100644 --- a/src/components/ui/colorpicker-input.tsx +++ b/src/components/ui/colorpicker-input.tsx @@ -1,9 +1,10 @@ +import { forwardRef } from "react"; + import { ColorPicker, parseColor, useColorPickerContext, } from "@chakra-ui/react"; -import { forwardRef } from "react"; export const ColorPickerInput = forwardRef< HTMLInputElement, diff --git a/src/components/ui/field.tsx b/src/components/ui/field.tsx index e6da02ac..82149505 100644 --- a/src/components/ui/field.tsx +++ b/src/components/ui/field.tsx @@ -1,6 +1,7 @@ -import { Field as ChakraField } from "@chakra-ui/react"; import { type ReactNode, forwardRef } from "react"; +import { Field as ChakraField } from "@chakra-ui/react"; + export interface FieldProps extends Omit { label?: ReactNode; helperText?: ReactNode; diff --git a/src/components/ui/provider.tsx b/src/components/ui/provider.tsx index ddf195c8..0a399a89 100644 --- a/src/components/ui/provider.tsx +++ b/src/components/ui/provider.tsx @@ -1,6 +1,7 @@ "use client"; import { ChakraProvider } from "@chakra-ui/react"; + import { ColorModeProvider, type ColorModeProviderProps } from "./color-mode"; import { system } from "./theme"; diff --git a/src/components/ui/radio.tsx b/src/components/ui/radio.tsx index 69da7c75..4581f0e3 100644 --- a/src/components/ui/radio.tsx +++ b/src/components/ui/radio.tsx @@ -1,6 +1,7 @@ -import { RadioGroup as ChakraRadioGroup } from "@chakra-ui/react"; import { type RefObject, type InputHTMLAttributes, forwardRef } from "react"; +import { RadioGroup as ChakraRadioGroup } from "@chakra-ui/react"; + export interface RadioProps extends ChakraRadioGroup.ItemProps { rootRef?: RefObject; inputProps?: InputHTMLAttributes; diff --git a/src/components/ui/tooltip.tsx b/src/components/ui/tooltip.tsx index 3cb3ae2e..9f4e4b29 100644 --- a/src/components/ui/tooltip.tsx +++ b/src/components/ui/tooltip.tsx @@ -1,6 +1,7 @@ -import { Tooltip as ChakraTooltip, Portal } from "@chakra-ui/react"; import { type RefObject, type ReactNode, forwardRef } from "react"; +import { Tooltip as ChakraTooltip, Portal } from "@chakra-ui/react"; + export interface TooltipProps extends ChakraTooltip.RootProps { showArrow?: boolean; portalled?: boolean; diff --git a/src/emotion/emotion-client.tsx b/src/emotion/emotion-client.tsx index 16e09813..9e985fe9 100644 --- a/src/emotion/emotion-client.tsx +++ b/src/emotion/emotion-client.tsx @@ -1,6 +1,3 @@ -/* eslint-disable react-refresh/only-export-components */ -import { CacheProvider } from "@emotion/react"; -import type { EmotionCache } from "@emotion/react"; import { createContext, useContext, @@ -9,6 +6,11 @@ import { useRef, useState, } from "react"; + +import type { EmotionCache } from "@emotion/react"; + +import { CacheProvider } from "@emotion/react"; + import { createEmotionCache } from "./emotion-cache"; export const ClientStyleContext = createContext({ diff --git a/src/emotion/emotion-server.tsx b/src/emotion/emotion-server.tsx index 9c53f733..6d53f403 100644 --- a/src/emotion/emotion-server.tsx +++ b/src/emotion/emotion-server.tsx @@ -1,6 +1,7 @@ import { CacheProvider } from "@emotion/react"; import createEmotionServer from "@emotion/server/create-instance"; import { renderToString } from "react-dom/server"; + import { Provider } from "../components/ui/provider"; import { createEmotionCache } from "./emotion-cache"; diff --git a/src/entry.client.tsx b/src/entry.client.tsx index 1fc935a8..e423fe9f 100644 --- a/src/entry.client.tsx +++ b/src/entry.client.tsx @@ -1,6 +1,8 @@ import { StrictMode, startTransition } from "react"; + import { hydrateRoot } from "react-dom/client"; import { HydratedRouter } from "react-router/dom"; + import { ClientCacheProvider } from "./emotion/emotion-client"; startTransition(() => { diff --git a/src/entry.server.tsx b/src/entry.server.tsx index 7824c3bf..254621a9 100644 --- a/src/entry.server.tsx +++ b/src/entry.server.tsx @@ -1,11 +1,14 @@ +import type { EntryContext } from "react-router"; +import { ServerRouter } from "react-router"; + +import type { RenderToPipeableStreamOptions } from "react-dom/server"; + import { CacheProvider } from "@emotion/react"; import { createReadableStreamFromReadable } from "@react-router/node"; import { isbot } from "isbot"; import { PassThrough } from "node:stream"; -import type { RenderToPipeableStreamOptions } from "react-dom/server"; import { renderToPipeableStream } from "react-dom/server"; -import type { EntryContext } from "react-router"; -import { ServerRouter } from "react-router"; + import { createEmotionCache } from "./emotion/emotion-cache"; export const streamTimeout = 5_000; @@ -21,11 +24,11 @@ export default function handleRequest( ) { return new Promise((resolve, reject) => { let shellRendered = false; - const userAgent = request.headers.get("user-agent"); + let userAgent = request.headers.get("user-agent"); // Ensure requests from bots and SPA Mode renders wait for all content to load before responding // https://react.dev/reference/react-dom/server/renderToPipeableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation - const readyOption: keyof RenderToPipeableStreamOptions = + let readyOption: keyof RenderToPipeableStreamOptions = (userAgent && isbot(userAgent)) || routerContext.isSpaMode ? "onAllReady" : "onShellReady"; @@ -55,7 +58,6 @@ export default function handleRequest( pipe(body); }, onShellError(error: unknown) { - // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors reject(error); }, onError(error: unknown) { diff --git a/src/lib/activity.ts b/src/lib/activity.ts index 74c2565b..b3b5a7c9 100644 --- a/src/lib/activity.ts +++ b/src/lib/activity.ts @@ -1,13 +1,15 @@ import type { EventInput } from "@fullcalendar/react"; + import { nanoid } from "nanoid"; +import type { Class } from "./class"; import type { ColorScheme } from "./colors"; +import type { PEClass } from "./pe"; +import type { RawSection, RawTimeslot } from "./raw"; + import { fallbackColor, textColor } from "./colors"; import { Slot } from "./dates"; -import type { RawSection, RawTimeslot } from "./raw"; import { sum } from "./utils"; -import type { PEClass } from "./pe"; -import type { Class } from "./class"; interface ActivityEventInput extends EventInput { room?: string; @@ -103,6 +105,21 @@ export interface BaseActivity { export type Activity = Class | PEClass | CustomActivity; +export function isActivity(obj: unknown): obj is Activity { + return ( + typeof obj === "object" && + obj !== null && + "id" in obj && + "name" in obj && + "shortName" in obj && + "backgroundColor" in obj && + "manualColor" in obj && + "hours" in obj && + "buttonName" in obj && + "events" in obj + ); +} + /** * A group of events to be rendered in a calendar, all of the same name, room, * and color. @@ -153,6 +170,13 @@ export class Event { } } +export type DeflatedCustomActivity = [ + timeslots: RawTimeslot[], + name: string, + backgroundColor: string, + room: string, +]; + /** A custom activity, created by the user. */ export class CustomActivity implements BaseActivity { /** ID unique over all Activities. */ @@ -210,8 +234,8 @@ export class CustomActivity implements BaseActivity { } /** Deflate an activity to something JSONable. */ - deflate(): (RawTimeslot[] | string)[] { - const res = [ + deflate(): DeflatedCustomActivity { + return [ this.timeslots.map((slot) => [ slot.startSlot.slot, slot.numSlots, @@ -220,20 +244,17 @@ export class CustomActivity implements BaseActivity { this.backgroundColor, this.room ?? "", ]; - return res; } /** Inflate a custom activity with info from the output of deflate. */ - inflate(parsed: (RawTimeslot[] | string)[]): void { + inflate(parsed: DeflatedCustomActivity): void { const [timeslots, name, backgroundColor, room] = parsed; - this.timeslots = (timeslots as RawTimeslot[]).map( - (slot) => new Timeslot(...slot), - ); - this.name = name as string; - this.room = (room as string) || undefined; + this.timeslots = timeslots.map((slot) => new Timeslot(...slot)); + this.name = name; + this.room = room || undefined; if (backgroundColor) { this.manualColor = true; - this.backgroundColor = backgroundColor as string; + this.backgroundColor = backgroundColor; } } } @@ -338,9 +359,9 @@ export class Sections { return this.kind ? this.kind.toLowerCase() : "sec"; } - private readonly _priority = 0; + private readonly priotityDefault = 0; get priority(): number { - return this._priority; + return this.priotityDefault; } /** Name for the kind of sections these are. */ diff --git a/src/lib/class.ts b/src/lib/class.ts index 7fb8088f..8a181c02 100644 --- a/src/lib/class.ts +++ b/src/lib/class.ts @@ -1,5 +1,29 @@ -import { Event, Sections, type BaseActivity, type Section } from "./activity"; import type { ColorScheme } from "./colors"; + +import bioImg from "../assets/bio.gif"; +import calc1Img from "../assets/calc1.gif"; +import calc2Img from "../assets/calc2.gif"; +import chemImg from "../assets/chem.gif"; +import cihImg from "../assets/cih.gif"; +import cihwImg from "../assets/cihw.gif"; +import fallImg from "../assets/fall.gif"; +import gradImg from "../assets/grad.gif"; +import hassAImg from "../assets/hassA.gif"; +import hassEImg from "../assets/hassE.gif"; +import hassHImg from "../assets/hassH.gif"; +import hassSImg from "../assets/hassS.gif"; +import iapImg from "../assets/iap.gif"; +import labImg from "../assets/lab.gif"; +import nonextImg from "../assets/nonext.gif"; +import partlabImg from "../assets/partLab.gif"; +import phys1Img from "../assets/phys1.gif"; +import phys2Img from "../assets/phys2.gif"; +import repeatImg from "../assets/repeat.gif"; +import restImg from "../assets/rest.gif"; +import springImg from "../assets/spring.gif"; +import summerImg from "../assets/summer.gif"; +import underImg from "../assets/under.gif"; +import { Event, Sections, type BaseActivity, type Section } from "./activity"; import { fallbackColor } from "./colors"; import { CI, @@ -12,30 +36,6 @@ import { type RawSection, } from "./raw"; -import nonextImg from "../assets/nonext.gif"; -import underImg from "../assets/under.gif"; -import gradImg from "../assets/grad.gif"; -import fallImg from "../assets/fall.gif"; -import iapImg from "../assets/iap.gif"; -import springImg from "../assets/spring.gif"; -import summerImg from "../assets/summer.gif"; -import repeatImg from "../assets/repeat.gif"; -import bioImg from "../assets/bio.gif"; -import calc1Img from "../assets/calc1.gif"; -import calc2Img from "../assets/calc2.gif"; -import chemImg from "../assets/chem.gif"; -import labImg from "../assets/lab.gif"; -import partlabImg from "../assets/partLab.gif"; -import phys1Img from "../assets/phys1.gif"; -import phys2Img from "../assets/phys2.gif"; -import restImg from "../assets/rest.gif"; -import hassHImg from "../assets/hassH.gif"; -import hassAImg from "../assets/hassA.gif"; -import hassSImg from "../assets/hassS.gif"; -import hassEImg from "../assets/hassE.gif"; -import cihImg from "../assets/cih.gif"; -import cihwImg from "../assets/cihw.gif"; - /** Flags. */ export interface Flags { nonext: boolean; @@ -113,6 +113,9 @@ export const getFlagImg = (flag: keyof Flags): string => { return flagImages[flag] ?? ""; }; +export type DeflatedClassEntry = string | string[] | number | null; +export type DeflatedClass = [string, ...DeflatedClassEntry[]]; + export class ClassSections extends Sections { declare cls: Class; declare kind: SectionKind; @@ -139,6 +142,10 @@ export class ClassSections extends Sections { return "lab"; case SectionKind.DESIGN: return "des"; + default: { + this.kind satisfies never; + throw new Error(`Invalid SectionKind`); + } } } @@ -152,6 +159,10 @@ export class ClassSections extends Sections { return 2; case SectionKind.DESIGN: return 3; + default: { + this.kind satisfies never; + throw new Error(`Invalid SectionKind`); + } } } @@ -165,6 +176,10 @@ export class ClassSections extends Sections { return "Lab"; case SectionKind.DESIGN: return "Design"; + default: { + this.kind satisfies never; + throw new Error(`Invalid SectionKind`); + } } } } @@ -218,9 +233,13 @@ export class Class implements BaseActivity { rawClass.designRawSections, rawClass.designSections, ); + default: { + kind satisfies never; + throw new Error(`Invalid SectionKind`); + } } }) - .sort((a, b) => a.priority - b.priority); + .toSorted((a, b) => a.priority - b.priority); this.backgroundColor = fallbackColor(colorScheme); } @@ -491,7 +510,7 @@ export class Class implements BaseActivity { } /** Deflate a class to something JSONable. */ - deflate() { + deflate(): DeflatedClass { const sections = this.sections.map((secs) => !secs.locked ? null @@ -503,12 +522,12 @@ export class Class implements BaseActivity { this.number, ...(this.manualColor ? [this.backgroundColor] : []), // string ...(sectionLocs.length ? [sectionLocs] : []), // array[string] - ...(sections.length > 0 ? (sections as number[]) : []), // number + ...(sections.length > 0 ? sections : []), // number | null ]; } /** Inflate a class with info from the output of deflate. */ - inflate(parsed: string | (string | number | string[])[]): void { + inflate(parsed: string | DeflatedClass): void { if (typeof parsed === "string") { // just the class number, ignore return; @@ -520,9 +539,10 @@ export class Class implements BaseActivity { this.backgroundColor = parsed[1]; this.manualColor = true; } - let sectionLocs: (string | number | string[])[] | null = null; - if (Array.isArray(parsed[offset])) { - sectionLocs = parsed[offset] as string[]; + let sectionLocs: string[] | null = null; + const parsedEntry = parsed[offset]; + if (Array.isArray(parsedEntry)) { + sectionLocs = parsedEntry; offset += 1; } this.sections.forEach((secs, i) => { @@ -534,7 +554,7 @@ export class Class implements BaseActivity { secs.locked = false; } else { secs.locked = true; - secs.selected = secs.sections[parse as number]; + secs.selected = secs.sections[Number(parse)]; } }); } diff --git a/src/lib/dates.ts b/src/lib/dates.ts index 9fab257f..f7633008 100644 --- a/src/lib/dates.ts +++ b/src/lib/dates.ts @@ -29,6 +29,10 @@ const TIMESLOTS = 34; /** Type of semester abbreviations. */ export type TSemester = keyof typeof SEMESTER_NAMES; +function isTSemester(value: unknown): value is TSemester { + return typeof value === "string" && value in SEMESTER_NAMES; +} + /** Strings for each weekday. */ export const WEEKDAY_STRINGS = ["Mon", "Tue", "Wed", "Thu", "Fri"]; @@ -141,9 +145,15 @@ export function parseUrlName(urlName: string): { year: string; semester: TSemester; } { + const semester = urlName[0]; + + if (!isTSemester(semester)) { + throw new Error(`Invalid semester: ${semester}`); + } + return { year: urlName.substring(1), - semester: urlName[0] as TSemester, + semester: semester, }; } @@ -171,6 +181,10 @@ function getLastUrlName(urlName: string): string { return `i${year}`; case "i": return `f${(parseInt(year, 10) - 1).toString()}`; + default: { + semester satisfies never; + throw new Error(`Invalid urlName: ${urlName}`); + } } } @@ -186,6 +200,10 @@ function getNextUrlName(urlName: string): string { return `f${year}`; case "f": return `i${(parseInt(year, 10) + 1).toString()}`; + default: { + semester satisfies never; + throw new Error(`Invalid urlName: ${urlName}`); + } } } diff --git a/src/lib/gapi.ts b/src/lib/gapi.ts index cda363e1..1edc1115 100644 --- a/src/lib/gapi.ts +++ b/src/lib/gapi.ts @@ -1,10 +1,12 @@ import type { ICalEventData } from "ical-generator"; + import { ICalCalendar } from "ical-generator"; import { RRuleTemporal } from "rrule-temporal"; import { tzlib_get_ical_block } from "timezones-ical-library"; import type { Activity } from "./activity"; import type { Term } from "./dates"; + import { Class } from "./class"; import { useHydrantContext } from "./hydrant"; @@ -98,7 +100,7 @@ export function useICSExport( try { download(`${state.term.urlName}.ics`, cal.toString()); - } catch (_err) { + } catch { onError?.(); } onSuccess?.(); diff --git a/src/lib/hydrant.ts b/src/lib/hydrant.ts index 1bfb69ed..bb7b19c7 100644 --- a/src/lib/hydrant.ts +++ b/src/lib/hydrant.ts @@ -1,11 +1,12 @@ import { createContext, useContext, useEffect, useRef, useState } from "react"; -import { useColorMode } from "../components/ui/color-mode"; import type { TermInfo } from "../lib/dates"; -import type { RawClass, RawPEClass, BuildingInfo } from "./raw"; import type { HydrantState } from "../lib/schema"; +import type { RawClass, RawPEClass, BuildingInfo } from "./raw"; + +import { useColorMode } from "../components/ui/color-mode"; import { DEFAULT_STATE } from "../lib/schema"; -import type { State } from "../lib/state"; +import { State } from "../lib/state"; export interface SemesterData { classes: Record; @@ -35,7 +36,7 @@ export const getStateMaps = ( /** Fetch from the url, which is JSON of type T. */ export const fetchNoCache = async (url: string): Promise => { const res = await fetch(url, { cache: "no-cache" }); - return (await res.json()) as T; + return await res.json(); }; /** Hook to fetch data and initialize State object. */ @@ -70,14 +71,18 @@ export function useHydrant({ globalState }: { globalState: State }): { } }; state.updateState(); + // oxlint-disable-next-line react-hooks/exhaustive-deps }, [colorMode, state]); return { state, hydrantState }; } -export const HydrantContext = createContext({ +export const HydrantContext = createContext<{ + hydrantState: HydrantState; + state: State; +}>({ hydrantState: DEFAULT_STATE, - state: {} as State, + state: Object.create(State.prototype), }); export const useHydrantContext = () => useContext(HydrantContext); diff --git a/src/lib/pe.ts b/src/lib/pe.ts index 27ee62ec..67207cd8 100644 --- a/src/lib/pe.ts +++ b/src/lib/pe.ts @@ -1,12 +1,13 @@ import type { ReactNode } from "react"; +import type { IconType } from "react-icons"; + +import { LuShipWheel, LuWand, LuWaves, LuLaptop } from "react-icons/lu"; import { Section, Sections, type BaseActivity } from "./activity"; -import { type RawPEClass, type RawSection } from "./raw"; import { Event } from "./activity"; +import { type DeflatedClassEntry } from "./class"; import { fallbackColor, type ColorScheme } from "./colors"; - -import type { IconType } from "react-icons"; -import { LuShipWheel, LuWand, LuWaves, LuLaptop } from "react-icons/lu"; +import { type RawPEClass, type RawSection } from "./raw"; export const W35_PLUS_TEXT = "W31, W32, W33, W34 and W35 are all connected. Enter through W35."; @@ -31,6 +32,8 @@ export const getPEFlagIcon = (flag: keyof PEFlags): ReactNode => { return (peFlagIcons[flag] ?? (() => null))({}); }; +export type DeflatedPEClass = [string, ...DeflatedClassEntry[]]; + export class PESection extends Section { sectionNumber: string; @@ -204,7 +207,7 @@ export class PEClass implements BaseActivity { } /** Deflate a class to something JSONable. */ - deflate() { + deflate(): DeflatedPEClass { const sections = this.sections.map((secs) => !secs.locked ? null @@ -216,11 +219,12 @@ export class PEClass implements BaseActivity { this.id, ...(this.manualColor ? [this.backgroundColor] : []), // string ...(sectionLocs.length ? [sectionLocs] : []), // array[string] - ...(sections.length > 0 ? (sections as number[]) : []), // number + ...(sections.length > 0 ? sections : []), // number | null ]; } - inflate(parsed: string | (string | number | string[])[]): void { + /** Inflate a class with info from the output of deflate. */ + inflate(parsed: string | DeflatedPEClass): void { if (typeof parsed === "string") { // just the class id, ignore return; @@ -232,9 +236,10 @@ export class PEClass implements BaseActivity { this.backgroundColor = parsed[1]; this.manualColor = true; } - let sectionLocs: (string | number | string[])[] | null = null; - if (Array.isArray(parsed[offset])) { - sectionLocs = parsed[offset] as string[]; + let sectionLocs: string[] | null = null; + const parsedEntry = parsed[offset]; + if (Array.isArray(parsedEntry)) { + sectionLocs = parsedEntry; offset += 1; } this.sections.forEach((secs, i) => { @@ -246,7 +251,7 @@ export class PEClass implements BaseActivity { secs.locked = false; } else { secs.locked = true; - secs.selected = secs.sections[parse as number]; + secs.selected = secs.sections[Number(parse)]; } }); } diff --git a/src/lib/state.ts b/src/lib/state.ts index 50ad747d..7113ab09 100644 --- a/src/lib/state.ts +++ b/src/lib/state.ts @@ -3,24 +3,47 @@ import { nanoid } from "nanoid"; import type { Timeslot, Activity, + DeflatedCustomActivity, Section, SectionLockOption, Sections, } from "./activity"; +import type { DeflatedClass } from "./class"; +import type { ColorScheme } from "./colors"; +import type { Term } from "./dates"; +import type { MeasurementSystem } from "./measurement"; +import type { DeflatedPEClass } from "./pe"; +import type { RawClass, RawPEClass, BuildingInfo } from "./raw"; +import type { HydrantState, Preferences, Save } from "./schema"; + import { CustomActivity } from "./activity"; import { scheduleSlots } from "./calendarSlots"; import { Class } from "./class"; -import type { Term } from "./dates"; -import type { ColorScheme } from "./colors"; import { chooseColors, fallbackColor, getDefaultColorScheme } from "./colors"; -import type { MeasurementSystem } from "./measurement"; import { getDefaultMeasurementSystem } from "./measurement"; -import type { RawClass, RawTimeslot, RawPEClass, BuildingInfo } from "./raw"; +import { PEClass } from "./pe"; +import { BANNER_LAST_CHANGED, DEFAULT_PREFERENCES } from "./schema"; import { Store } from "./store"; import { sum, urldecode, urlencode } from "./utils"; -import type { HydrantState, Preferences, Save } from "./schema"; -import { BANNER_LAST_CHANGED, DEFAULT_PREFERENCES } from "./schema"; -import { PEClass } from "./pe"; + +export type DeflatedProgramState = [ + (string | DeflatedClass)[], + DeflatedCustomActivity[] | null, + number | undefined, + (string | DeflatedPEClass)[] | undefined, // undefined for backwards compatability +]; + +function isDeflatedProgramState(obj: unknown): obj is DeflatedProgramState { + if (!Array.isArray(obj) || !(obj.length === 4 || obj.length === 3)) + return false; + const [classes, customActivities, option, peClasses] = obj; + if (!Array.isArray(classes)) return false; + if (customActivities !== null && !Array.isArray(customActivities)) + return false; + if (option !== undefined && typeof option !== "number") return false; + if (peClasses !== undefined && !Array.isArray(peClasses)) return false; + return true; +} /** * Global State object. Maintains global program state (selected classes, @@ -224,13 +247,13 @@ export class State { /** Rename a given non-activity. */ renameCustomActivity(customActivity: CustomActivity, name: string): void { - const customActivity_ = this.selectedCustomActivities.find( + const customActivityFound = this.selectedCustomActivities.find( (customActivity_) => customActivity_.id === customActivity.id, ); - if (!customActivity_) return; + if (!customActivityFound) return; - customActivity_.name = name; + customActivityFound.name = name; this.updateState(); } @@ -239,13 +262,13 @@ export class State { customActivity: CustomActivity, room: string | undefined, ): void { - const customActivity_ = this.selectedCustomActivities.find( + const customActivityFound = this.selectedCustomActivities.find( (customActivity_) => customActivity_.id === customActivity.id, ); - if (!customActivity_) return; + if (!customActivityFound) return; - customActivity_.room = room; + customActivityFound.room = room; this.updateState(); } @@ -437,7 +460,7 @@ export class State { 4: 4, }; - const sortedQuarters = Array.from(allQuarters).sort((a, b) => { + const sortedQuarters = Array.from(allQuarters).toSorted((a, b) => { return quarterOrder[a] - quarterOrder[b]; }); @@ -456,7 +479,7 @@ export class State { } /** Deflate program state to something JSONable. */ - deflate() { + deflate(): DeflatedProgramState { return [ this.selectedClasses.map((cls) => cls.deflate()), this.selectedCustomActivities.length > 0 @@ -470,33 +493,18 @@ export class State { } /** Parse all program state. */ - inflate( - obj: - | ( - | number - | (string | number | string[])[][] - | (string | RawTimeslot[])[][] - | null - )[] - | null, - ): void { + inflate(obj: DeflatedProgramState | null): void { if (!obj) return; this.reset(); - const [classes, customActivities, selectedOption, peClasses] = obj as [ - (string | number | string[])[][], - (string | RawTimeslot[])[][] | null, - number | undefined, - (string | number | string[])[][] | undefined, // undefined for backwards compatability - ]; + const [classes, customActivities, selectedOption, peClasses] = obj; for (const deflated of classes) { const cls = typeof deflated === "string" ? this.classes.get(deflated) - : this.classes.get((deflated as string[])[0]); + : this.classes.get(deflated[0]); // if we can't find the class, add it to unknownSubjects so we can show a warning if (!cls) { - const subject = - typeof deflated === "string" ? deflated : (deflated as string[])[0]; + const subject = typeof deflated === "string" ? deflated : deflated[0]; this.unknownSubjects.add(subject); continue; @@ -518,14 +526,12 @@ export class State { const cls = typeof deflated === "string" ? (this.peClasses.get(deflated) ?? - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions this.peClasses.get(`Q3.${deflated}`)) - : (this.peClasses.get((deflated as string[])[0]) ?? - this.peClasses.get(`Q3.${(deflated as string[])[0]}`)); + : (this.peClasses.get(deflated[0]) ?? + this.peClasses.get(`Q3.${deflated[0]}`)); // if we can't find the class, add it to unknownSubjects so we can show a warning if (!cls) { - const subject = - typeof deflated === "string" ? deflated : (deflated as string[])[0]; + const subject = typeof deflated === "string" ? deflated : deflated[0]; this.unknownSubjects.add(subject); continue; @@ -548,7 +554,8 @@ export class State { } const storage = this.store.get(id); if (!storage) return; - this.inflate(storage as Parameters[0]); + if (!isDeflatedProgramState(storage)) return; + this.inflate(storage); this.saveId = id; this.updateState(false); } @@ -579,7 +586,7 @@ export class State { /** Rename a given save. */ renameSave(id: string, name: string): void { - const save = this.saves.find((save) => save.id === id); + const save = this.saves.find((s) => s.id === id); if (!save || !name) return; save.name = name; this.storeSave(); @@ -635,14 +642,19 @@ export class State { this.saves = []; this.addSave(true); } - if (save) { - this.inflate(urldecode(save) as Parameters[0]); + let decoded: unknown; + if ( + save && + (decoded = urldecode(save)) && + isDeflatedProgramState(decoded) + ) { + this.inflate(decoded); } else { // Try to load default schedule if set, otherwise load first save const defaultScheduleId = this.preferences.defaultScheduleId; if ( defaultScheduleId && - this.saves.some((save) => save.id === defaultScheduleId) + this.saves.some((s) => s.id === defaultScheduleId) ) { this.loadSave(defaultScheduleId); } else { @@ -654,5 +666,10 @@ export class State { if (storedStarred) { this.starredClasses = new Set(storedStarred); } + // Load starred PE classes from storage + const storedStarredPE = this.store.get("starredPEClasses"); + if (storedStarredPE) { + this.starredPEClasses = new Set(storedStarredPE); + } } } diff --git a/src/lib/store.ts b/src/lib/store.ts index 25dfd175..f7c5f17e 100644 --- a/src/lib/store.ts +++ b/src/lib/store.ts @@ -1,10 +1,14 @@ import type { Preferences, Save } from "./schema"; +import type { DeflatedProgramState } from "./state"; export interface TermStore { saves: Save[]; /** Array of class numbers that are starred */ starredClasses: string[]; - [saveId: string]: unknown[]; + /** Array of PE class numbers that are starred */ + starredPEClasses: string[]; + /** Array of program states that are saved */ + [saveId: string]: Save[] | string[] | DeflatedProgramState; } export interface GlobalStore { @@ -28,13 +32,13 @@ export class Store { /** Return the corresponding, term-specific saved value. */ get(key: T): TermStore[T] | null { const result = localStorage.getItem(this.toKey(key.toString(), false)); - return result !== null ? (JSON.parse(result) as TermStore[T]) : null; + return result !== null ? JSON.parse(result) : null; } /** Return the corresponding global saved value. */ globalGet(key: T): GlobalStore[T] | null { const result = localStorage.getItem(this.toKey(key, true)); - return result !== null ? (JSON.parse(result) as GlobalStore[T]) : null; + return result !== null ? JSON.parse(result) : null; } /** Set the corresponding term-specific value. */ diff --git a/src/lib/utils.tsx b/src/lib/utils.tsx index 7db79e88..a49977ba 100644 --- a/src/lib/utils.tsx +++ b/src/lib/utils.tsx @@ -1,6 +1,7 @@ +import type { JSX } from "react/jsx-runtime"; + import { Link } from "@chakra-ui/react"; import { unpack, pack } from "msgpackr"; -import type { JSX } from "react/jsx-runtime"; import type { State } from "./state"; diff --git a/src/root.tsx b/src/root.tsx index d8aa2a86..6c97e809 100644 --- a/src/root.tsx +++ b/src/root.tsx @@ -6,19 +6,19 @@ import { Scripts, ScrollRestoration, } from "react-router"; -import type { Route } from "./+types/root"; +import { Flex, Spinner, Text, Stack, Code } from "@chakra-ui/react"; import { withEmotionCache } from "@emotion/react"; -import { useInjectStyles } from "./emotion/emotion-client"; + +import type { Route } from "./+types/root"; import { Provider } from "./components/ui/provider"; -import { Flex, Spinner, Text, Stack, Code } from "@chakra-ui/react"; +import { useInjectStyles } from "./emotion/emotion-client"; +import "temporal-polyfill/global"; import "@fontsource-variable/ibm-plex-sans/index.css"; import "@fontsource/ibm-plex-mono/index.css"; -import "temporal-polyfill/global"; - export const links: Route.LinksFunction = () => [ { rel: "icon", @@ -35,10 +35,6 @@ export function HydrateFallback() { ); } -interface LayoutProps { - children: React.ReactNode; -} - function Analytics() { return ( <> @@ -55,20 +51,31 @@ function Analytics() { ); } -// eslint-disable-next-line react-refresh/only-export-components -export const Layout = withEmotionCache((props: LayoutProps, cache) => { +interface LayoutProps { + children: React.ReactNode; +} + +export const Layout = (props: LayoutProps) => { + return ; +}; + +const LayoutWithCache = withEmotionCache((props: LayoutProps, cache) => { const { children } = props; useInjectStyles(cache); return ( - - + + Hydrant + diff --git a/src/routes/_index.tsx b/src/routes/_index.tsx index 2c83a50a..a2a3ba42 100644 --- a/src/routes/_index.tsx +++ b/src/routes/_index.tsx @@ -3,32 +3,32 @@ import { Flex, Group, ButtonGroup, - SkipNavContent, SkipNavLink, + SkipNavContent, } from "@chakra-ui/react"; -import { Calendar } from "../components/Calendar"; -import { LeftFooter } from "../components/Footers"; -import { Header, PreferencesDialog } from "../components/Header"; -import { SelectedActivities } from "../components/SelectedActivities"; -import { ScheduleOption } from "../components/ScheduleOption"; -import { ScheduleSwitcher } from "../components/ScheduleSwitcher"; -import { TermSwitcher } from "../components/TermSwitcher"; + +import type { Route } from "./+types/_index"; + +import { ActivityDescription } from "../components/ActivityDescription"; import { Banner } from "../components/Banner"; import { MatrixLink, PreregLink, ExportCalendar, } from "../components/ButtonsLinks"; +import { Calendar } from "../components/Calendar"; import { ClassTypesSwitcher } from "../components/ClassTypes"; - -import { State } from "../lib/state"; +import { LeftFooter } from "../components/Footers"; +import { Header, PreferencesDialog } from "../components/Header"; +import { ScheduleOption } from "../components/ScheduleOption"; +import { ScheduleSwitcher } from "../components/ScheduleSwitcher"; +import { SelectedActivities } from "../components/SelectedActivities"; +import { TermSwitcher } from "../components/TermSwitcher"; import { Term } from "../lib/dates"; +import { getClosestUrlName, type LatestTermInfo } from "../lib/dates"; import { type SemesterData, getStateMaps } from "../lib/hydrant"; import { useHydrant, HydrantContext, fetchNoCache } from "../lib/hydrant"; -import { getClosestUrlName, type LatestTermInfo } from "../lib/dates"; - -import type { Route } from "./+types/_index"; -import { ActivityDescription } from "~/components/ActivityDescription"; +import { State } from "../lib/state"; export async function clientLoader({ url }: Route.ClientLoaderArgs) { const searchParams = url.searchParams; diff --git a/src/routes/export.ts b/src/routes/export.ts index 61693965..86dad506 100644 --- a/src/routes/export.ts +++ b/src/routes/export.ts @@ -1,11 +1,11 @@ import { redirect } from "react-router"; -import { fetchNoCache, type SemesterData, getStateMaps } from "../lib/hydrant"; +import type { Route } from "./+types/export"; + +import { Class } from "../lib/class"; import { getClosestUrlName, Term, type LatestTermInfo } from "../lib/dates"; +import { fetchNoCache, type SemesterData, getStateMaps } from "../lib/hydrant"; import { State } from "../lib/state"; -import { Class } from "../lib/class"; - -import type { Route } from "./+types/export"; /** * "Integration callbacks" allow other SIPB projects to integrate with Hydrant by redirecting to diff --git a/src/routes/overrides.($prefillId).tsx b/src/routes/overrides.($prefillId).tsx index 6130aaa5..6a339b47 100644 --- a/src/routes/overrides.($prefillId).tsx +++ b/src/routes/overrides.($prefillId).tsx @@ -1,16 +1,8 @@ import { useCallback, useMemo, useState } from "react"; +import { Link as RouterLink } from "react-router"; -import Form from "@rjsf/chakra-ui"; import type { CustomValidator, RJSFSchema, UiSchema } from "@rjsf/utils"; -import validator from "@rjsf/validator-ajv8"; import type { JSONSchema7Definition } from "json-schema"; -import TOML from "smol-toml"; - -import { Link as RouterLink } from "react-router"; -import type { Route } from "./+types/overrides.($prefillId)"; - -import logo from "../assets/logo.svg"; -import itemSchema from "../../scrapers/overrides.toml.d/override-schema.json"; import { Container, @@ -27,19 +19,43 @@ import { SkipNavContent, SkipNavLink, } from "@chakra-ui/react"; +import { Theme as ChakraUITheme } from "@rjsf/chakra-ui"; +import { withTheme } from "@rjsf/core"; +import { customizeValidator } from "@rjsf/validator-ajv8"; +import { stringify as tomlStringify, parse as tomlParse } from "smol-toml"; + +import type { Route } from "./+types/overrides.($prefillId)"; + +import { + additionalProperties, + definitions, +} from "../../scrapers/overrides.toml.d/override-schema.json"; +import logo from "../assets/logo.svg"; + +type FormData = Record[]; + +const Form = withTheme(ChakraUITheme); +const validator = customizeValidator(); const schema: RJSFSchema = { title: "Overrides", type: "array", + // oxlint-disable-next-line typescript/no-unsafe-type-assertion items: { - ...itemSchema.additionalProperties, + ...additionalProperties, required: ["number"], } as unknown as JSONSchema7Definition, - $defs: itemSchema.$defs as Record, + // oxlint-disable-next-line typescript/no-unsafe-type-assertion + definitions: definitions as Record, }; -const overrides = Object.assign( - {}, +type OverridesRecord = Record< + string, + { name: string; data: () => Promise } +>; + +const overrides: OverridesRecord = Object.assign( + {} as OverridesRecord, ...Object.entries( import.meta.glob("../../scrapers/overrides.toml.d/**/*.toml", { query: "raw", @@ -51,7 +67,7 @@ const overrides = Object.assign( return { [key]: { name, data } }; }), -) as Record Promise }>; +); const overridesCollection = createListCollection({ items: Object.entries(overrides) @@ -59,7 +75,7 @@ const overridesCollection = createListCollection({ label: name, value: key, })) - .sort((a, b) => a.label.localeCompare(b.label)), + .toSorted((a, b) => a.label.localeCompare(b.label)), }); const overrideNames = Object.entries(overrides) @@ -69,13 +85,14 @@ const overrideNames = Object.entries(overrides) return accum; }, {}); -const getDataFromFile = async (fileName: string) => { +const getDataFromFileAsync = async (fileName: string) => { try { const textToml = await overrides[fileName].data(); - const mod = TOML.parse(textToml); + const mod = tomlParse(textToml); - const newData = Object.entries(mod).map(([key, value_1]) => { - const { number: num, ...rest } = value_1 as Record; + const newData = Object.entries(mod).map(([key, value]) => { + // oxlint-disable-next-line typescript/no-unsafe-type-assertion + const { number: _num, ...rest } = value as Record; return { number: key, ...rest, @@ -89,10 +106,7 @@ const getDataFromFile = async (fileName: string) => { }; /** ensures class numbers are unique, since we change override from object to array */ -const validateUniqueNumbers: CustomValidator[]> = ( - formData, - errors, -) => { +const validateUniqueNumbers: CustomValidator = (formData, errors) => { const indicesByNumber = new Map(); (formData ?? []).forEach((override, index) => { const number = override.number; @@ -116,13 +130,15 @@ const validateUniqueNumbers: CustomValidator[]> = ( }; export async function clientLoader({ params }: Route.ClientLoaderArgs) { - let prefillData: Record[] = []; + let prefillData: FormData = []; const prefillIdPrelim = params.prefillId?.toUpperCase(); let prefillId = ""; if (prefillIdPrelim) { if (Object.keys(overrideNames).includes(prefillIdPrelim)) { - const newData = await getDataFromFile(overrideNames[prefillIdPrelim]); + const newData = await getDataFromFileAsync( + overrideNames[prefillIdPrelim], + ); if (newData.length > 0) { prefillData = newData; prefillId = overrideNames[prefillIdPrelim]; @@ -146,7 +162,7 @@ export default function App({ loaderData }: Route.ComponentProps) { const [selected, setSelected] = useState([prefillId]); // TODO IN NEXT COMMIT: Make ui schema match what it did before :( - const uischema = useMemo(() => { + const uischema = useMemo>(() => { const uiSchema = { "ui:title": "Overrides", "ui:submitButtonOptions": { @@ -155,7 +171,6 @@ export default function App({ loaderData }: Route.ComponentProps) { }, submitText: "Download", }, - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment items: { "ui:title": "Class Override", "ui:field": "LayoutGridField", @@ -272,7 +287,7 @@ export default function App({ loaderData }: Route.ComponentProps) { "ui:field": "LayoutHeaderField", }, ...Object.fromEntries( - Object.entries(itemSchema.additionalProperties.properties).map( + Object.entries(additionalProperties.properties).map( ([key, value]) => { if ("description" in value) { return [ @@ -295,34 +310,12 @@ export default function App({ loaderData }: Route.ComponentProps) { }, } satisfies UiSchema; - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access uiSchema.items.description["ui:widget"] = "textarea"; return uiSchema; }, [data.length, error]); - const getDataFromFile = useCallback( - async (fileName: string) => { - try { - const textToml = await overrides[fileName].data(); - const mod = TOML.parse(textToml); - - const newData = Object.entries(mod).map(([key, value_1]) => { - const { number: num, ...rest } = value_1 as Record; - return { - number: key, - ...rest, - }; - }); - return newData; - } catch (err) { - console.error("Error loading TOML file:", err); - return []; - } - }, - [overrides], - ); - + const getDataFromFile = useCallback(getDataFromFileAsync, []); const handleChange = (e: Select.ValueChangeDetails) => { const fileName = e.value[0]; @@ -428,13 +421,13 @@ export default function App({ loaderData }: Route.ComponentProps) { liveOmit={"onChange"} omitExtraData={true} onChange={({ formData, errors }) => { - setData(formData as Record[]); - setError(errors.length > 0 ? true : false); + setData(formData ?? []); + setError(errors.length > 0); }} onSubmit={() => { const contents = "#:schema ../override-schema.json\n\n" + - TOML.stringify( + tomlStringify( Object.fromEntries( data.map((override) => { const { number: num, ...rest } = override; diff --git a/tests/activity.test.ts b/tests/activity.test.ts index 9cf51332..ceb8c806 100644 --- a/tests/activity.test.ts +++ b/tests/activity.test.ts @@ -1,12 +1,13 @@ import { expect, test, describe } from "vitest"; + import { Timeslot, CustomActivity, Event } from "../src/lib/activity"; -import { Slot } from "../src/lib/dates"; import { COLOR_SCHEME_LIGHT, COLOR_SCHEME_DARK, COLOR_SCHEME_LIGHT_CONTRAST, COLOR_SCHEME_DARK_CONTRAST, } from "../src/lib/colors"; +import { Slot } from "../src/lib/dates"; import "temporal-polyfill/global"; diff --git a/tests/class.test.ts b/tests/class.test.ts index 6b5b13a4..fd0ed19d 100644 --- a/tests/class.test.ts +++ b/tests/class.test.ts @@ -1,5 +1,13 @@ import { expect, test } from "vitest"; -import { type Flags, getFlagImg, Class, ClassSections } from "../src/lib/class"; + +import { + type DeflatedClass, + type Flags, + getFlagImg, + Class, + ClassSections, +} from "../src/lib/class"; +import { COLOR_SCHEME_LIGHT } from "../src/lib/colors"; import { CI, GIR, @@ -9,7 +17,6 @@ import { TermCode, type RawClass, } from "../src/lib/raw"; -import { COLOR_SCHEME_LIGHT } from "../src/lib/colors"; // auxiliary object for testing getFlagImg; change as needed const flagNameValidity: [keyof Flags, boolean][] = [ @@ -248,6 +255,7 @@ describe("Class", () => { expect(myClass.new).toEqual(false); }); + // oxlint-disable-next-line vitest/warn-todo test.todo("Class.events"); test("Class.flags", () => { @@ -567,11 +575,9 @@ describe("Class", () => { * - has section room overrides, doesn't have section room overrides */ - type Deflated = (string | number | string[])[]; - test("no unlocked sections, no manual color, no section room overrides", () => { const myClass: Class = new Class(myRawClass, COLOR_SCHEME_LIGHT); - const expectedDeflated: Deflated = ["21H.143", [""]]; + const expectedDeflated: DeflatedClass = ["21H.143", [""]]; expect(myClass.deflate()).toStrictEqual(expectedDeflated); const myOtherClass: Class = new Class( @@ -587,7 +593,7 @@ describe("Class", () => { const mySections: ClassSections | undefined = myClass.sections.at(0); assert(mySections instanceof ClassSections); mySections.locked = true; - const expectedDeflated: Deflated = ["21H.143", [""], -1]; + const expectedDeflated: DeflatedClass = ["21H.143", [""], -1]; expect(myClass.deflate()).toStrictEqual(expectedDeflated); const myOtherClass: Class = new Class( @@ -606,7 +612,7 @@ describe("Class", () => { test("has manual color", () => { const myClass: Class = new Class(myRawClass, COLOR_SCHEME_LIGHT); myClass.manualColor = true; - const expectedDeflated: Deflated = ["21H.143", "#475569", [""]]; + const expectedDeflated: DeflatedClass = ["21H.143", "#475569", [""]]; expect(myClass.deflate()).toStrictEqual(expectedDeflated); const myOtherClass: Class = new Class( @@ -622,7 +628,7 @@ describe("Class", () => { const mySections: ClassSections | undefined = myClass.sections.at(0); assert(mySections instanceof ClassSections); mySections.roomOverride = "lorem"; - const expectedDeflated: Deflated = ["21H.143", ["lorem"]]; + const expectedDeflated: DeflatedClass = ["21H.143", ["lorem"]]; expect(myClass.deflate()).toStrictEqual(expectedDeflated); const myOtherClass: Class = new Class( diff --git a/tests/colors.test.ts b/tests/colors.test.ts index 8a1a54c8..8f3df902 100644 --- a/tests/colors.test.ts +++ b/tests/colors.test.ts @@ -1,4 +1,5 @@ import { test, describe, expect, vi } from "vitest"; + import { COLOR_SCHEME_LIGHT, COLOR_SCHEME_DARK, @@ -31,6 +32,29 @@ describe("fallbackColor", () => { }); }); +// a function to create window.matchMedia mocks on the fly +function makeMatchMediaMock(matchMediaMap: Map): void { + // the mock function to assign to window.matchMedia + function matchMediaMock(query: string): MediaQueryList { + return { + matches: matchMediaMap.get(query) ?? false, + media: query, + onchange: null, + addListener: vi.fn(), + removeListener: vi.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + }; + } + // actually assign this mock function! + Object.defineProperty(window, "matchMedia", { + writable: true, + configurable: true, + value: matchMediaMock, + }); +} + describe("getDefaultColorScheme", () => { /** * Partition: @@ -38,29 +62,6 @@ describe("getDefaultColorScheme", () => { * - prefers-contrast: no-preference, more */ - // a function to create window.matchMedia mocks on the fly - function makeMatchMediaMock(matchMediaMap: Map): void { - // the mock function to assign to window.matchMedia - function matchMediaMock(query: string): MediaQueryList { - return { - matches: matchMediaMap.get(query) ?? false, - media: query, - onchange: null, - addListener: vi.fn(), - removeListener: vi.fn(), - addEventListener: vi.fn(), - removeEventListener: vi.fn(), - dispatchEvent: vi.fn(), - }; - } - // actually assign this mock function! - Object.defineProperty(window, "matchMedia", { - writable: true, - configurable: true, - value: matchMediaMock, - }); - } - test("prefers-color-scheme = light, prefers-constrast = no-preference", () => { makeMatchMediaMock( new Map([ diff --git a/tests/dates.test.ts b/tests/dates.test.ts index 716b9955..12b6f974 100644 --- a/tests/dates.test.ts +++ b/tests/dates.test.ts @@ -1,4 +1,5 @@ import { test, describe, beforeEach, expect } from "vitest"; + import { parseUrlName, getClosestUrlName, @@ -7,7 +8,6 @@ import { Slot, Term, } from "../src/lib/dates"; -import { JSDOM } from "jsdom"; import "temporal-polyfill/global"; @@ -773,7 +773,7 @@ describe("toFullUrl", () => { * - urlName, latestUrlName: same, different */ - assert(jsdom instanceof JSDOM); // otherwise eslint doesn't know what kind of thing `jsdom` is + assert(jsdom); beforeEach(() => { // Reset URL before each test jsdom.reconfigure({ url: "http://localhost/" }); diff --git a/tests/state.test.ts b/tests/state.test.ts index affd80e8..3703b622 100644 --- a/tests/state.test.ts +++ b/tests/state.test.ts @@ -1,5 +1,5 @@ import { expect, test, describe } from "vitest"; -import { State } from "../src/lib/state"; + import { Class } from "../src/lib/class"; import { COLOR_SCHEME_LIGHT } from "../src/lib/colors"; import { @@ -11,6 +11,7 @@ import { TermCode, type RawClass, } from "../src/lib/raw"; +import { State } from "../src/lib/state"; const baseRawClass: RawClass = { number: "21H.143", @@ -76,8 +77,8 @@ function makeClass(number: string, hasFinal: boolean): Class { } function makeStateWithClasses(classes: Class[]): State { - const state = Object.create(State.prototype) as State; - (state as unknown as { selectedClasses: Class[] }).selectedClasses = classes; + const state = Object.create(State.prototype); + state.selectedClasses = classes; return state; } @@ -123,17 +124,11 @@ function makeStateWithStarredClasses( starredClasses: Set, classes: Class[], ): State { - const state = Object.create(State.prototype) as State; - (state as unknown as { starredClasses: Set }).starredClasses = - starredClasses; - (state as unknown as { store: { set: () => void } }).store = { - set: () => undefined, - }; - (state as unknown as { updateState: () => void }).updateState = () => - undefined; - (state as unknown as { classes: Map }).classes = new Map( - classes.map((cls) => [cls.number, cls]), - ); + const state = Object.create(State.prototype); + state.starredClasses = starredClasses; + state.store = { set: () => undefined }; + state.updateState = () => undefined; + state.classes = new Map(classes.map((cls) => [cls.number, cls])); return state; } diff --git a/tests/store.test.ts b/tests/store.test.ts index 8e9e2059..9c90f76c 100644 --- a/tests/store.test.ts +++ b/tests/store.test.ts @@ -1,7 +1,9 @@ import { afterEach, expect, describe, test } from "vitest"; -import { Store } from "../src/lib/store"; + import type { Preferences } from "../src/lib/schema"; +import { Store } from "../src/lib/store"; + // note that jsdom supports localStorage since v11.12.0; c.f. github.com/jsdom/jsdom/blob/main/Changelog.md describe("Store", () => { // create a Store object once and for all, since they touch the same localStorage anyways diff --git a/tests/utils.test.ts b/tests/utils.test.ts index 6fd1cfb1..d772a496 100644 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -1,4 +1,5 @@ import { test, describe, expect } from "vitest"; + import { classSort, classNumberMatch, diff --git a/tsconfig.json b/tsconfig.json index 7b0d00c5..1808eb3d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,34 +1,18 @@ { - "include": [ - "**/*", - "**/.server/**/*", - "**/.client/**/*", - ".react-router/types/**/*" + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.vite.json" + } ], "compilerOptions": { - "lib": [ - "DOM", - "DOM.Iterable", - "ES2022", - "esnext.temporal", - "esnext.intl", - "esnext.date" - ], - "types": ["node", "vite/client", "vitest/globals", "vitest/jsdom"], - "target": "ES2022", - "module": "ES2022", - "moduleResolution": "bundler", - "jsx": "react-jsx", - "rootDirs": [".", "./.react-router/types"], - "paths": { - "~/*": ["./src/*"] - }, - "customConditions": ["dev"], - "esModuleInterop": true, + "checkJs": true, "verbatimModuleSyntax": true, - "noEmit": true, - "resolveJsonModule": true, "skipLibCheck": true, - "strict": true + "strict": true, + "noEmit": true } } diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 00000000..6a124a37 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "include": ["vite.config.ts"], + "compilerOptions": { + "composite": true, + "strict": true, + "types": ["node"], + "lib": ["ES2023"], + "target": "ES2023", + "module": "esnext", + "moduleResolution": "bundler" + } +} diff --git a/tsconfig.vite.json b/tsconfig.vite.json new file mode 100644 index 00000000..6ff072bf --- /dev/null +++ b/tsconfig.vite.json @@ -0,0 +1,33 @@ +{ + "extends": "./tsconfig.json", + "include": [ + ".react-router/types/**/*", + "src/**/*", + "src/**/.server/**/*", + "src/**/.client/**/*", + "tests/**/*" + ], + "compilerOptions": { + "composite": true, + "strict": true, + "lib": [ + "DOM", + "DOM.Iterable", + "ES2023", + "esnext.temporal", + "esnext.intl", + "esnext.date" + ], + "types": ["vite/client", "vitest/globals", "vitest/jsdom"], + "target": "ES2023", + "module": "esnext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "rootDirs": [".", "./.react-router/types"], + "paths": { + "~/*": ["./src/*"] + }, + "esModuleInterop": true, + "resolveJsonModule": true + } +}