From 612200174fcf5aed9157b65a2868f9b1fefac203 Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Thu, 12 Jun 2025 17:30:58 +0100 Subject: [PATCH 01/37] config eslint --- eslint.config.js | 53 ++ package.json | 9 +- yarn.lock | 1818 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 1871 insertions(+), 9 deletions(-) create mode 100644 eslint.config.js diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..2b1257d --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,53 @@ +import js from '@eslint/js' +import tseslint from '@typescript-eslint/eslint-plugin' +import tsParser from '@typescript-eslint/parser' +import globals from 'globals' + +export default [ + js.configs.recommended, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parser: tsParser, + parserOptions: { + project: './tsconfig.json', + ecmaVersion: 2020, + sourceType: 'module' + }, + globals: { + ...globals.browser, + ...globals.node + } + }, + plugins: { + '@typescript-eslint': tseslint + }, + rules: { + // No trailing semi-colons + 'semi': ['error', 'never'], + + // Single quotes for strings + 'quotes': ['error', 'single', { 'avoidEscape': true }], + + // Other standard rules + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-unused-vars': ['warn', { 'argsIgnorePattern': '^_' }], + 'no-console': ['warn', { allow: ['warn', 'error'] }], + + // Additional rules based on user preferences + 'indent': ['error', 2], + 'object-curly-spacing': ['error', 'always'], + 'array-bracket-spacing': ['error', 'never'], + 'comma-dangle': ['error', 'never'], + 'arrow-parens': ['error', 'always'], + 'no-multiple-empty-lines': ['error', { 'max': 1, 'maxEOF': 0 }] + } + }, + { + // Special rules for JSON files + files: ['**/*.json'], + rules: { + 'quotes': ['error', 'double'] + } + } +] diff --git a/package.json b/package.json index 82a9695..fd8eeef 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,9 @@ "test:watch": "vitest", "test:coverage": "vitest run --coverage", "test:e2e": "playwright test tests/e2e/demo.spec.ts", - "test:e2e:ui": "playwright test --ui tests/e2e/demo.spec.ts" + "test:e2e:ui": "playwright test --ui tests/e2e/demo.spec.ts", + "lint": "eslint 'src/**/*.{ts,tsx}'", + "lint:fix": "eslint 'src/**/*.{ts,tsx}' --fix" }, "devDependencies": { "@playwright/test": "^1.53.0", @@ -25,8 +27,13 @@ "@storybook/html-vite": "^9.0.6", "@storybook/test": "^8.6.14", "@types/fs-extra": "^11.0.4", + "@typescript-eslint/eslint-plugin": "^8.34.0", + "@typescript-eslint/parser": "^8.34.0", "@vitest/coverage-v8": "^3.2.3", "@vitest/ui": "^3.2.3", + "eslint": "^9.28.0", + "eslint-config-standard-with-typescript": "^43.0.1", + "eslint-plugin-import": "^2.31.0", "fs-extra": "^11.3.0", "jsdom": "^26.1.0", "storybook": "^9.0.6", diff --git a/yarn.lock b/yarn.lock index 6e843f2..6099132 100644 --- a/yarn.lock +++ b/yarn.lock @@ -223,6 +223,107 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz#7fc114af5f6563f19f73324b5d5ff36ece0803d1" integrity sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g== +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.7.0": + version "4.7.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz#607084630c6c033992a082de6e6fbc1a8b52175a" + integrity sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/config-array@^0.20.0": + version "0.20.1" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.20.1.tgz#454f89be82b0e5b1ae872c154c7e2f3dd42c3979" + integrity sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw== + dependencies: + "@eslint/object-schema" "^2.1.6" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/config-helpers@^0.2.1": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.2.3.tgz#39d6da64ed05d7662659aa7035b54cd55a9f3672" + integrity sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg== + +"@eslint/core@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.14.0.tgz#326289380968eaf7e96f364e1e4cf8f3adf2d003" + integrity sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/core@^0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.15.0.tgz#8fc04709a7b9a179d9f7d93068fc000cb8c5603d" + integrity sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/eslintrc@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964" + integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@9.28.0": + version "9.28.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.28.0.tgz#7822ccc2f8cae7c3cd4f902377d520e9ae03f844" + integrity sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg== + +"@eslint/object-schema@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" + integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== + +"@eslint/plugin-kit@^0.3.1": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz#0cad96b134d23a653348e3342f485636b5ef4732" + integrity sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg== + dependencies: + "@eslint/core" "^0.15.0" + levn "^0.4.1" + +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/retry@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" + integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== + +"@humanwhocodes/retry@^0.4.2": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" + integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -287,6 +388,27 @@ dependencies: "@types/mdx" "^2.0.0" +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -404,6 +526,11 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.43.0.tgz#42a88207659e404e8ffa655cae763cbad94906ab" integrity sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw== +"@rtsao/scc@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" + integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== + "@storybook/addon-backgrounds@9.0.0-alpha.12": version "9.0.0-alpha.12" resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-9.0.0-alpha.12.tgz#ad75263d15dbe19a69be35e38afa331bce1b6fd0" @@ -608,7 +735,7 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8" integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ== -"@types/estree@^1.0.0": +"@types/estree@^1.0.0", "@types/estree@^1.0.6": version "1.0.8" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== @@ -621,6 +748,16 @@ "@types/jsonfile" "*" "@types/node" "*" +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + "@types/jsonfile@*": version "6.1.4" resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.4.tgz#614afec1a1164e7d670b4a7ad64df3e7beb7b702" @@ -647,6 +784,149 @@ dependencies: csstype "^3.0.2" +"@typescript-eslint/eslint-plugin@^8.34.0": + version "8.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz#96c9f818782fe24cd5883a5d517ca1826d3fa9c2" + integrity sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.34.0" + "@typescript-eslint/type-utils" "8.34.0" + "@typescript-eslint/utils" "8.34.0" + "@typescript-eslint/visitor-keys" "8.34.0" + graphemer "^1.4.0" + ignore "^7.0.0" + natural-compare "^1.4.0" + ts-api-utils "^2.1.0" + +"@typescript-eslint/parser@^6.4.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" + integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== + dependencies: + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + +"@typescript-eslint/parser@^8.34.0": + version "8.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.34.0.tgz#703270426ac529304ae6988482f487c856d9c13f" + integrity sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA== + dependencies: + "@typescript-eslint/scope-manager" "8.34.0" + "@typescript-eslint/types" "8.34.0" + "@typescript-eslint/typescript-estree" "8.34.0" + "@typescript-eslint/visitor-keys" "8.34.0" + debug "^4.3.4" + +"@typescript-eslint/project-service@8.34.0": + version "8.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.34.0.tgz#449119b72fe9fae185013a6bdbaf1ffbfee6bcaf" + integrity sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw== + dependencies: + "@typescript-eslint/tsconfig-utils" "^8.34.0" + "@typescript-eslint/types" "^8.34.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" + integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== + dependencies: + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + +"@typescript-eslint/scope-manager@8.34.0": + version "8.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz#9fedaec02370cf79c018a656ab402eb00dc69e67" + integrity sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw== + dependencies: + "@typescript-eslint/types" "8.34.0" + "@typescript-eslint/visitor-keys" "8.34.0" + +"@typescript-eslint/tsconfig-utils@8.34.0", "@typescript-eslint/tsconfig-utils@^8.34.0": + version "8.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz#97d0a24e89a355e9308cebc8e23f255669bf0979" + integrity sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA== + +"@typescript-eslint/type-utils@8.34.0": + version "8.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz#03e7eb3776129dfd751ba1cac0c6ea4b0fab5ec6" + integrity sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg== + dependencies: + "@typescript-eslint/typescript-estree" "8.34.0" + "@typescript-eslint/utils" "8.34.0" + debug "^4.3.4" + ts-api-utils "^2.1.0" + +"@typescript-eslint/types@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" + integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== + +"@typescript-eslint/types@8.34.0", "@typescript-eslint/types@^8.34.0": + version "8.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.34.0.tgz#18000f205c59c9aff7f371fc5426b764cf2890fb" + integrity sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA== + +"@typescript-eslint/typescript-estree@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" + integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== + dependencies: + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "9.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/typescript-estree@8.34.0": + version "8.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz#c9f3feec511339ef64e9e4884516c3e558f1b048" + integrity sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg== + dependencies: + "@typescript-eslint/project-service" "8.34.0" + "@typescript-eslint/tsconfig-utils" "8.34.0" + "@typescript-eslint/types" "8.34.0" + "@typescript-eslint/visitor-keys" "8.34.0" + debug "^4.3.4" + fast-glob "^3.3.2" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^2.1.0" + +"@typescript-eslint/utils@8.34.0": + version "8.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.34.0.tgz#7844beebc1153b4d3ec34135c2da53a91e076f8d" + integrity sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ== + dependencies: + "@eslint-community/eslint-utils" "^4.7.0" + "@typescript-eslint/scope-manager" "8.34.0" + "@typescript-eslint/types" "8.34.0" + "@typescript-eslint/typescript-estree" "8.34.0" + +"@typescript-eslint/visitor-keys@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" + integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== + dependencies: + "@typescript-eslint/types" "6.21.0" + eslint-visitor-keys "^3.4.1" + +"@typescript-eslint/visitor-keys@8.34.0": + version "8.34.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz#c7a149407be31d755dba71980617d638a40ac099" + integrity sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA== + dependencies: + "@typescript-eslint/types" "8.34.0" + eslint-visitor-keys "^4.2.0" + "@vitest/coverage-v8@^3.2.3": version "3.2.3" resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-3.2.3.tgz#866f6d9b7d394dc614f3f2186dbe46b1bc940813" @@ -823,7 +1103,12 @@ loupe "^3.1.2" tinyrainbow "^1.2.0" -acorn@^8.14.0: +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.14.0, acorn@^8.15.0: version "8.15.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== @@ -833,6 +1118,16 @@ agent-base@^7.1.0, agent-base@^7.1.2: resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + 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" + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" @@ -860,6 +1155,11 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + aria-query@5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" @@ -872,6 +1172,79 @@ aria-query@^5.0.0: resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== +array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" + integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== + dependencies: + call-bound "^1.0.3" + is-array-buffer "^3.0.5" + +array-includes@^3.1.8: + version "3.1.9" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.9.tgz#1f0ccaa08e90cdbc3eb433210f903ad0f17c3f3a" + integrity sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.4" + define-properties "^1.2.1" + es-abstract "^1.24.0" + es-object-atoms "^1.1.1" + get-intrinsic "^1.3.0" + is-string "^1.1.1" + math-intrinsics "^1.1.0" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.findlastindex@^1.2.5: + version "1.2.6" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz#cfa1065c81dcb64e34557c9b81d012f6a421c564" + integrity sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.4" + define-properties "^1.2.1" + es-abstract "^1.23.9" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + es-shim-unscopables "^1.1.0" + +array.prototype.flat@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz#534aaf9e6e8dd79fb6b9a9917f839ef1ec63afe5" + integrity sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-shim-unscopables "^1.0.2" + +array.prototype.flatmap@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" + integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-shim-unscopables "^1.0.2" + +arraybuffer.prototype.slice@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" + integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + is-array-buffer "^3.0.4" + assertion-error@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" @@ -893,6 +1266,18 @@ ast-v8-to-istanbul@^0.3.3: estree-walker "^3.0.3" js-tokens "^9.0.1" +async-function@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" + integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -905,6 +1290,14 @@ better-opn@^3.0.2: dependencies: open "^8.0.4" +brace-expansion@^1.1.7: + version "1.1.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + brace-expansion@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" @@ -912,6 +1305,13 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -922,6 +1322,37 @@ cac@^6.7.14: resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== +call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bind@^1.0.7, call-bind@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== + dependencies: + call-bind-apply-helpers "^1.0.0" + es-define-property "^1.0.0" + get-intrinsic "^1.2.4" + set-function-length "^1.2.2" + +call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + chai@^5.1.1, chai@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/chai/-/chai-5.2.0.tgz#1358ee106763624114addf84ab02697e411c9c05" @@ -941,7 +1372,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -971,6 +1402,11 @@ commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" @@ -1006,13 +1442,47 @@ data-urls@^5.0.0: whatwg-mimetype "^4.0.0" whatwg-url "^14.0.0" -debug@4, debug@^4.1.1, debug@^4.3.4, debug@^4.4.1: +data-view-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" + integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + +data-view-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" + integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + +data-view-byte-offset@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" + integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +debug@4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== dependencies: ms "^2.1.3" +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + decimal.js@^10.5.0: version "10.5.0" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.5.0.tgz#0f371c7cf6c4898ce0afb09836db73cd82010f22" @@ -1023,16 +1493,53 @@ deep-eql@^5.0.1: resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + dequal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + dom-accessibility-api@^0.5.9: version "0.5.16" resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" @@ -1043,6 +1550,15 @@ dom-accessibility-api@^0.6.3: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w== +dunder-proto@^1.0.0, dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -1063,11 +1579,114 @@ entities@^6.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.1.tgz#c28c34a43379ca7f61d074130b2f5f7020a30694" integrity sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g== +es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9, es-abstract@^1.24.0: + version "1.24.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328" + integrity sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg== + dependencies: + array-buffer-byte-length "^1.0.2" + arraybuffer.prototype.slice "^1.0.4" + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + data-view-buffer "^1.0.2" + data-view-byte-length "^1.0.2" + data-view-byte-offset "^1.0.1" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + es-set-tostringtag "^2.1.0" + es-to-primitive "^1.3.0" + function.prototype.name "^1.1.8" + get-intrinsic "^1.3.0" + get-proto "^1.0.1" + get-symbol-description "^1.1.0" + globalthis "^1.0.4" + gopd "^1.2.0" + has-property-descriptors "^1.0.2" + has-proto "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + internal-slot "^1.1.0" + is-array-buffer "^3.0.5" + is-callable "^1.2.7" + is-data-view "^1.0.2" + is-negative-zero "^2.0.3" + is-regex "^1.2.1" + is-set "^2.0.3" + is-shared-array-buffer "^1.0.4" + is-string "^1.1.1" + is-typed-array "^1.1.15" + is-weakref "^1.1.1" + math-intrinsics "^1.1.0" + object-inspect "^1.13.4" + object-keys "^1.1.1" + object.assign "^4.1.7" + own-keys "^1.0.1" + regexp.prototype.flags "^1.5.4" + safe-array-concat "^1.1.3" + safe-push-apply "^1.0.0" + safe-regex-test "^1.1.0" + set-proto "^1.0.0" + stop-iteration-iterator "^1.1.0" + string.prototype.trim "^1.2.10" + string.prototype.trimend "^1.0.9" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.3" + typed-array-byte-length "^1.0.3" + typed-array-byte-offset "^1.0.4" + typed-array-length "^1.0.7" + unbox-primitive "^1.1.0" + which-typed-array "^1.1.19" + +es-define-property@^1.0.0, es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + es-module-lexer@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +es-shim-unscopables@^1.0.2, es-shim-unscopables@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz#438df35520dac5d105f3943d927549ea3b00f4b5" + integrity sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw== + dependencies: + hasown "^2.0.2" + +es-to-primitive@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" + integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== + dependencies: + is-callable "^1.2.7" + is-date-object "^1.0.5" + is-symbol "^1.0.4" + esbuild-register@^3.5.0: version "3.6.0" resolved "https://registry.yarnpkg.com/esbuild-register/-/esbuild-register-3.6.0.tgz#cf270cfa677baebbc0010ac024b823cbf723a36d" @@ -1106,11 +1725,157 @@ esbuild-register@^3.5.0: "@esbuild/win32-ia32" "0.25.5" "@esbuild/win32-x64" "0.25.5" +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-standard-with-typescript@^43.0.1: + version "43.0.1" + resolved "https://registry.yarnpkg.com/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-43.0.1.tgz#977862d7d41b0e1f27f399137bbf7b2e017037ff" + integrity sha512-WfZ986+qzIzX6dcr4yGUyVb/l9N3Z8wPXCc5z/70fljs3UbWhhV+WxrfgsqMToRzuuyX9MqZ974pq2UPhDTOcA== + dependencies: + "@typescript-eslint/parser" "^6.4.0" + eslint-config-standard "17.1.0" + +eslint-config-standard@17.1.0: + version "17.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz#40ffb8595d47a6b242e07cbfd49dc211ed128975" + integrity sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q== + +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-module-utils@^2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz#fe4cfb948d61f49203d7b08871982b65b9af0b0b" + integrity sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg== + dependencies: + debug "^3.2.7" + +eslint-plugin-import@^2.31.0: + version "2.31.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7" + integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A== + dependencies: + "@rtsao/scc" "^1.1.0" + array-includes "^3.1.8" + array.prototype.findlastindex "^1.2.5" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.12.0" + hasown "^2.0.2" + is-core-module "^2.15.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.8" + object.groupby "^1.0.3" + object.values "^1.2.0" + semver "^6.3.1" + string.prototype.trimend "^1.0.8" + tsconfig-paths "^3.15.0" + +eslint-scope@^8.3.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" + integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.2.0, eslint-visitor-keys@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" + integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== + +eslint@^9.28.0: + version "9.28.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.28.0.tgz#b0bcbe82a16945a40906924bea75e8b4980ced7d" + integrity sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.20.0" + "@eslint/config-helpers" "^0.2.1" + "@eslint/core" "^0.14.0" + "@eslint/eslintrc" "^3.3.1" + "@eslint/js" "9.28.0" + "@eslint/plugin-kit" "^0.3.1" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.4.2" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.6" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.3.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + +espree@^10.0.1, espree@^10.3.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" + integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== + dependencies: + acorn "^8.15.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.1" + esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +esquery@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + estree-walker@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" @@ -1118,11 +1883,49 @@ estree-walker@^3.0.3: dependencies: "@types/estree" "^1.0.0" +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + expect-type@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.1.tgz#af76d8b357cf5fa76c41c09dafb79c549e75f71f" integrity sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw== +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9, fast-glob@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.8" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.19.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" + integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== + dependencies: + reusify "^1.0.4" + fdir@^6.4.4: version "6.4.6" resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.6.tgz#2b268c0232697063111bbf3f64810a2a741ba281" @@ -1133,11 +1936,48 @@ fflate@^0.8.2: resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== -flatted@^3.3.3: +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + +flatted@^3.2.9, flatted@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== +for-each@^0.3.3, for-each@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" + integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== + dependencies: + is-callable "^1.2.7" + foreground-child@^3.1.0: version "3.3.1" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" @@ -1165,6 +2005,75 @@ fsevents@~2.3.2, fsevents@~2.3.3: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" + integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + functions-have-names "^1.2.3" + hasown "^2.0.2" + is-callable "^1.2.7" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.0, get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +get-symbol-description@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" + integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob@^10.4.1: version "10.4.5" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" @@ -1177,16 +2086,89 @@ glob@^10.4.1: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globalthis@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +gopd@^1.0.1, gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-bigints@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" + integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg== + has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" + integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== + dependencies: + dunder-proto "^1.0.0" + +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + html-encoding-sniffer@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" @@ -1222,26 +2204,240 @@ iconv-lite@0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +ignore@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +ignore@^7.0.0: + version "7.0.5" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" + integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== + +import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + indent-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== +internal-slot@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" + integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.2" + side-channel "^1.1.0" + +is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" + integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + +is-async-function@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" + integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ== + dependencies: + async-function "^1.0.0" + call-bound "^1.0.3" + get-proto "^1.0.1" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" + +is-bigint@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" + integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== + dependencies: + has-bigints "^1.0.2" + +is-boolean-object@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" + integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.13.0, is-core-module@^2.15.1, is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + +is-data-view@^1.0.1, is-data-view@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" + integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== + dependencies: + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + is-typed-array "^1.1.13" + +is-date-object@^1.0.5, is-date-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" + integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== + dependencies: + call-bound "^1.0.2" + has-tostringtag "^1.0.2" + is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-finalizationregistry@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" + integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== + dependencies: + call-bound "^1.0.3" + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-generator-function@^1.0.10: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.0.tgz#bf3eeda931201394f57b5dba2800f91a238309ca" + integrity sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ== + dependencies: + call-bound "^1.0.3" + get-proto "^1.0.0" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + +is-number-object@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" + integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + is-potential-custom-element-name@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== +is-regex@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== + dependencies: + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + +is-shared-array-buffer@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" + integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== + dependencies: + call-bound "^1.0.3" + +is-string@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" + integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-symbol@^1.0.4, is-symbol@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" + integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== + dependencies: + call-bound "^1.0.2" + has-symbols "^1.1.0" + safe-regex-test "^1.1.0" + +is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" + integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== + dependencies: + which-typed-array "^1.1.16" + +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + +is-weakref@^1.0.2, is-weakref@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" + integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== + dependencies: + call-bound "^1.0.3" + +is-weakset@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca" + integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ== + dependencies: + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" @@ -1249,6 +2445,11 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -1304,6 +2505,13 @@ js-tokens@^9.0.1: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.1.tgz#2ec43964658435296f6761b34e10671c2d9527f4" integrity sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ== +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsdom@^26.1.0: version "26.1.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-26.1.0.tgz#ab5f1c1cafc04bd878725490974ea5e8bf0c72b3" @@ -1330,6 +2538,28 @@ jsdom@^26.1.0: ws "^8.18.0" xml-name-validator "^5.0.0" +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -1339,6 +2569,33 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -1387,6 +2644,11 @@ map-or-similar@^1.5.0: resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08" integrity sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg== +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + memoizerific@^1.11.3: version "1.11.3" resolved "https://registry.yarnpkg.com/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a" @@ -1394,11 +2656,38 @@ memoizerific@^1.11.3: dependencies: map-or-similar "^1.5.0" +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + min-indent@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== +minimatch@9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" @@ -1406,6 +2695,11 @@ minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" @@ -1416,7 +2710,7 @@ mrmime@^2.0.0: resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.1.tgz#bc3e87f7987853a54c9850eeb1f1078cd44adddc" integrity sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ== -ms@^2.1.3: +ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -1426,11 +2720,67 @@ nanoid@^3.3.11: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + nwsapi@^2.2.16: version "2.2.20" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.20.tgz#22e53253c61e7b0e7e93cef42c891154bcca11ef" integrity sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA== +object-inspect@^1.13.3, object-inspect@^1.13.4: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.7: + version "4.1.7" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" + integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + has-symbols "^1.1.0" + object-keys "^1.1.1" + +object.fromentries@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" + integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + +object.groupby@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" + integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + +object.values@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.1.tgz#deed520a50809ff7f75a7cfd4bc64c7a038c6216" + integrity sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + open@^8.0.4: version "8.4.2" resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" @@ -1440,11 +2790,53 @@ open@^8.0.4: is-docker "^2.1.1" is-wsl "^2.2.0" +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + 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" + +own-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" + integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== + dependencies: + get-intrinsic "^1.2.6" + object-keys "^1.1.1" + safe-push-apply "^1.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + package-json-from-dist@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parse5@^7.2.1: version "7.3.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05" @@ -1452,11 +2844,21 @@ parse5@^7.2.1: dependencies: entities "^6.0.0" +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + path-scurry@^1.11.1: version "1.11.1" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" @@ -1465,6 +2867,11 @@ path-scurry@^1.11.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + pathe@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" @@ -1480,6 +2887,11 @@ picocolors@^1.1.1: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + picomatch@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" @@ -1506,6 +2918,11 @@ polished@^4.2.2: dependencies: "@babel/runtime" "^7.17.8" +possible-typed-array-names@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" + integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== + postcss@^8.5.3: version "8.5.5" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.5.tgz#04de7797f6911fb1c96550e96616d08681537ef3" @@ -1515,6 +2932,11 @@ postcss@^8.5.3: picocolors "^1.1.1" source-map-js "^1.2.1" +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + pretty-format@^27.0.2: version "27.5.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" @@ -1524,11 +2946,16 @@ pretty-format@^27.0.2: ansi-styles "^5.0.0" react-is "^17.0.1" -punycode@^2.3.1: +punycode@^2.1.0, punycode@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0": version "19.1.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.1.0.tgz#133558deca37fa1d682708df8904b25186793623" @@ -1565,6 +2992,51 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" + integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.9" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.7" + get-proto "^1.0.1" + which-builtin-type "^1.2.1" + +regexp.prototype.flags@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" + integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-errors "^1.3.0" + get-proto "^1.0.1" + gopd "^1.2.0" + set-function-name "^2.0.2" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@^1.22.4: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" + integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== + rollup@^4.34.9: version "4.43.0" resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.43.0.tgz#275c09119eb7eaf0c3dea040523b81ef43c57b8c" @@ -1599,6 +3071,41 @@ rrweb-cssom@^0.8.0: resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz#3021d1b4352fbf3b614aaeed0bc0d5739abe0bc2" integrity sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw== +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-array-concat@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" + integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + has-symbols "^1.1.0" + isarray "^2.0.5" + +safe-push-apply@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" + integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== + dependencies: + es-errors "^1.3.0" + isarray "^2.0.5" + +safe-regex-test@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-regex "^1.2.1" + "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -1616,11 +3123,47 @@ scheduler@^0.26.0: resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.26.0.tgz#4ce8a8c2a2095f13ea11bf9a445be50c555d6337" integrity sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA== -semver@^7.5.3, semver@^7.6.2: +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2: version "7.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== +set-function-length@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +set-proto@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" + integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== + dependencies: + dunder-proto "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -1633,6 +3176,46 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + siginfo@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" @@ -1657,6 +3240,11 @@ sirv@^3.0.1: mrmime "^2.0.0" totalist "^3.0.0" +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + source-map-js@^1.2.0, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" @@ -1685,6 +3273,14 @@ std-env@^3.9.0: resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.9.0.tgz#1a6f7243b339dca4c9fd55e1c7504c77ef23e8f1" integrity sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw== +stop-iteration-iterator@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" + integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ== + dependencies: + es-errors "^1.3.0" + internal-slot "^1.1.0" + storybook@^9.0.6: version "9.0.9" resolved "https://registry.yarnpkg.com/storybook/-/storybook-9.0.9.tgz#9131fd2e67def87dacb1f374d3c7ea7a6d3494d8" @@ -1729,6 +3325,38 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" +string.prototype.trim@^1.2.10: + version "1.2.10" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" + integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-data-property "^1.1.4" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-object-atoms "^1.0.0" + has-property-descriptors "^1.0.2" + +string.prototype.trimend@^1.0.8, string.prototype.trimend@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" + integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + "strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -1750,6 +3378,11 @@ strip-ansi@^7.0.1: dependencies: ansi-regex "^6.0.1" +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -1757,6 +3390,11 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + strip-literal@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-3.0.0.tgz#ce9c452a91a0af2876ed1ae4e583539a353df3fc" @@ -1771,6 +3409,11 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -1855,6 +3498,13 @@ tldts@^6.1.32: dependencies: tldts-core "^6.1.86" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + totalist@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" @@ -1874,21 +3524,103 @@ tr46@^5.1.0: dependencies: punycode "^2.3.1" +ts-api-utils@^1.0.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" + integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== + +ts-api-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" + integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== + ts-dedent@^2.0.0, ts-dedent@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + tslib@^2.0.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +typed-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" + integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-typed-array "^1.1.14" + +typed-array-byte-length@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" + integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== + dependencies: + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.14" + +typed-array-byte-offset@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" + integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.15" + reflect.getprototypeof "^1.0.9" + +typed-array-length@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" + integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + reflect.getprototypeof "^1.0.6" + typescript@~5.8.3: version "5.8.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== +unbox-primitive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" + integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== + dependencies: + call-bound "^1.0.3" + has-bigints "^1.0.2" + has-symbols "^1.1.0" + which-boxed-primitive "^1.1.1" + undici-types@~7.8.0: version "7.8.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.8.0.tgz#de00b85b710c54122e44fbfd911f8d70174cd294" @@ -1907,6 +3639,13 @@ unplugin@^1.3.1: acorn "^8.14.0" webpack-virtual-modules "^0.6.2" +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + vite-node@3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.2.3.tgz#1c5a2282fe100114c26fd221daf506e69d392a36" @@ -1998,6 +3737,59 @@ whatwg-url@^14.0.0, whatwg-url@^14.1.1: tr46 "^5.1.0" webidl-conversions "^7.0.0" +which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" + integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== + dependencies: + is-bigint "^1.1.0" + is-boolean-object "^1.2.1" + is-number-object "^1.1.1" + is-string "^1.1.1" + is-symbol "^1.1.1" + +which-builtin-type@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" + integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== + dependencies: + call-bound "^1.0.2" + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" + is-async-function "^2.0.0" + is-date-object "^1.1.0" + is-finalizationregistry "^1.1.0" + is-generator-function "^1.0.10" + is-regex "^1.2.1" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.1.0" + which-collection "^1.0.2" + which-typed-array "^1.1.16" + +which-collection@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== + dependencies: + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" + +which-typed-array@^1.1.16, which-typed-array@^1.1.19: + version "1.1.19" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" + integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + for-each "^0.3.5" + get-proto "^1.0.1" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -2013,6 +3805,11 @@ why-is-node-running@^2.3.0: siginfo "^2.0.0" stackback "0.0.2" +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -2045,3 +3842,8 @@ xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 4d8837e5afea45d86baf26c9f48c173a5773ccd0 Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Thu, 12 Jun 2025 17:31:06 +0100 Subject: [PATCH 02/37] fix bulk issues --- src/__tests__/enrichments/frequency.test.ts | 90 ++--- src/core/__tests__/table-detection.test.ts | 126 +++--- src/core/__tests__/type-detection.test.ts | 214 +++++----- src/core/table-detection.ts | 57 ++- src/core/table-processor.ts | 36 +- src/core/type-detection.ts | 118 +++--- .../__tests__/heatmap-utils.test.ts | 178 ++++---- src/enrichments/__tests__/heatmap.test.ts | 252 ++++++------ src/enrichments/frequency.ts | 70 ++-- src/enrichments/heatmap-utils.ts | 60 +-- src/enrichments/heatmap.ts | 312 +++++++------- src/enrichments/statistics.ts | 16 +- src/index.ts | 126 +++--- src/main.ts | 30 +- src/stories/Button.stories.ts | 44 +- src/stories/Button.ts | 22 +- src/stories/EnrichmentMenu.stories.ts | 72 ++-- src/stories/TableToggle.stories.ts | 96 ++--- src/test-setup.ts | 12 +- src/ui/enrichment-menu.ts | 72 ++-- src/ui/frequency-chart-dialog.ts | 380 +++++++++--------- src/ui/frequency-dialog.ts | 218 +++++----- src/ui/header-utils.ts | 144 +++---- src/ui/statistics-popup.ts | 181 +++++---- src/ui/toggle-injector.ts | 254 ++++++------ 25 files changed, 1585 insertions(+), 1595 deletions(-) diff --git a/src/__tests__/enrichments/frequency.test.ts b/src/__tests__/enrichments/frequency.test.ts index 2b950af..13ce200 100644 --- a/src/__tests__/enrichments/frequency.test.ts +++ b/src/__tests__/enrichments/frequency.test.ts @@ -1,13 +1,13 @@ -import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { describe, it, expect, beforeEach, afterEach } from 'vitest' import { applyFrequencyAnalysis, removeFrequencyAnalysis, toggleFrequencyAnalysis, isFrequencyAnalysisActive -} from '../../enrichments/frequency'; +} from '../../enrichments/frequency' describe('Frequency Analysis', () => { - let table: HTMLTableElement; + let table: HTMLTableElement // Create a test table with categorical data beforeEach(() => { @@ -42,94 +42,94 @@ describe('Frequency Analysis', () => { - `; + ` - table = document.getElementById('test-table') as HTMLTableElement; - }); + table = document.getElementById('test-table') as HTMLTableElement + }) afterEach(() => { - document.body.innerHTML = ''; - }); + document.body.innerHTML = '' + }) describe('applyFrequencyAnalysis', () => { it('calculates frequencies for a categorical column', () => { // Column 0 is the categorical column (Category) - const result = applyFrequencyAnalysis(table, 0, 'column'); + const result = applyFrequencyAnalysis(table, 0, 'column') // Expected frequencies: A: 2, B: 2, C: 1 expect(result).toEqual([ ['a', 2, 40], ['b', 2, 40], ['c', 1, 20] - ]); + ]) // Check that the header is marked - const header = table.rows[0].cells[0]; - expect(header.classList.contains('gs-frequency-header')).toBe(true); - }); + const header = table.rows[0].cells[0] + expect(header.classList.contains('gs-frequency-header')).toBe(true) + }) it('throws an error for non-categorical data', () => { // Column 1 is the numeric column (Value) expect(() => { - applyFrequencyAnalysis(table, 1, 'column'); - }).toThrow('Frequency analysis can only be applied to categorical data'); - }); - }); + applyFrequencyAnalysis(table, 1, 'column') + }).toThrow('Frequency analysis can only be applied to categorical data') + }) + }) describe('removeFrequencyAnalysis', () => { it('removes frequency analysis from a column', () => { // Apply frequency analysis - applyFrequencyAnalysis(table, 0, 'column'); + applyFrequencyAnalysis(table, 0, 'column') // Remove it - removeFrequencyAnalysis(table, 0, 'column'); + removeFrequencyAnalysis(table, 0, 'column') // Check that the header class is removed - const header = table.rows[0].cells[0]; - expect(header.classList.contains('gs-frequency-header')).toBe(false); + const header = table.rows[0].cells[0] + expect(header.classList.contains('gs-frequency-header')).toBe(false) // Check that the frequency info is removed - expect(table._frequencyInfos).toBeUndefined(); - }); + expect(table._frequencyInfos).toBeUndefined() + }) it('removes all frequency analyses when no index/type is provided', () => { // Apply multiple frequency analyses - applyFrequencyAnalysis(table, 0, 'column'); + applyFrequencyAnalysis(table, 0, 'column') // Add a test for row analysis if needed // Remove all - removeFrequencyAnalysis(table); + removeFrequencyAnalysis(table) // Check that all headers are cleaned up - const header = table.rows[0].cells[0]; - expect(header.classList.contains('gs-frequency-header')).toBe(false); - expect(table._frequencyInfos).toBeUndefined(); - }); - }); + const header = table.rows[0].cells[0] + expect(header.classList.contains('gs-frequency-header')).toBe(false) + expect(table._frequencyInfos).toBeUndefined() + }) + }) describe('toggleFrequencyAnalysis', () => { it('toggles frequency analysis on and off', () => { // First toggle - should apply - const result1 = toggleFrequencyAnalysis(table, 0, 'column'); - expect(result1).toBeDefined(); - expect(isFrequencyAnalysisActive(table, 0, 'column')).toBe(true); + const result1 = toggleFrequencyAnalysis(table, 0, 'column') + expect(result1).toBeDefined() + expect(isFrequencyAnalysisActive(table, 0, 'column')).toBe(true) // Second toggle - should remove - const result2 = toggleFrequencyAnalysis(table, 0, 'column'); - expect(result2).toBeUndefined(); - expect(isFrequencyAnalysisActive(table, 0, 'column')).toBe(false); - }); - }); + const result2 = toggleFrequencyAnalysis(table, 0, 'column') + expect(result2).toBeUndefined() + expect(isFrequencyAnalysisActive(table, 0, 'column')).toBe(false) + }) + }) describe('isFrequencyAnalysisActive', () => { it('returns true when frequency analysis is active', () => { - applyFrequencyAnalysis(table, 0, 'column'); - expect(isFrequencyAnalysisActive(table, 0, 'column')).toBe(true); - }); + applyFrequencyAnalysis(table, 0, 'column') + expect(isFrequencyAnalysisActive(table, 0, 'column')).toBe(true) + }) it('returns false when frequency analysis is not active', () => { - expect(isFrequencyAnalysisActive(table, 0, 'column')).toBe(false); - }); - }); -}); + expect(isFrequencyAnalysisActive(table, 0, 'column')).toBe(false) + }) + }) +}) diff --git a/src/core/__tests__/table-detection.test.ts b/src/core/__tests__/table-detection.test.ts index 64bfadb..a2e3292 100644 --- a/src/core/__tests__/table-detection.test.ts +++ b/src/core/__tests__/table-detection.test.ts @@ -1,96 +1,96 @@ -import { describe, it, expect, beforeEach } from 'vitest'; -import { findSuitableTables, isTableSuitable } from '../table-detection'; +import { describe, it, expect, beforeEach } from 'vitest' +import { findSuitableTables, isTableSuitable } from '../table-detection' describe('Table Detection', () => { // Helper to create a test table with the specified content const createTestTable = (rows: string[][], hasThead = true): HTMLTableElement => { - const table = document.createElement('table'); + const table = document.createElement('table') if (hasThead) { - const thead = table.createTHead(); - const headerRow = thead.insertRow(); + const thead = table.createTHead() + const headerRow = thead.insertRow() if (rows.length > 0) { - rows[0].forEach(cellText => { - const th = document.createElement('th'); - th.textContent = cellText; - headerRow.appendChild(th); - }); + rows[0].forEach((cellText) => { + const th = document.createElement('th') + th.textContent = cellText + headerRow.appendChild(th) + }) } } - const tbody = table.createTBody(); - const startRow = hasThead ? 1 : 0; + const tbody = table.createTBody() + const startRow = hasThead ? 1 : 0 for (let i = startRow; i < rows.length; i++) { - const row = tbody.insertRow(); - rows[i].forEach(cellText => { - const cell = row.insertCell(); - cell.textContent = cellText; - }); + const row = tbody.insertRow() + rows[i].forEach((cellText) => { + const cell = row.insertCell() + cell.textContent = cellText + }) } - return table; - }; + return table + } beforeEach(() => { // Clean up any existing tables - document.body.innerHTML = ''; - }); + document.body.innerHTML = '' + }) describe('isTableSuitable', () => { it('should return false for tables without thead and tbody', () => { - const table = document.createElement('table'); - const row = table.insertRow(); - row.insertCell().textContent = 'Header'; - row.insertCell().textContent = 'Value'; + const table = document.createElement('table') + const row = table.insertRow() + row.insertCell().textContent = 'Header' + row.insertCell().textContent = 'Value' - const result = isTableSuitable(table); - expect(result.isSuitable).toBe(false); - expect(result.reason).toContain('must have both and elements'); - }); + const result = isTableSuitable(table) + expect(result.isSuitable).toBe(false) + expect(result.reason).toContain('must have both and elements') + }) it('should return false for tables with no data rows', () => { const table = createTestTable([ ['Name', 'Age', 'Score'] - ]); + ]) - const result = isTableSuitable(table); - expect(result.isSuitable).toBe(false); - expect(result.reason).toContain('must have at least one data row'); - }); + const result = isTableSuitable(table) + expect(result.isSuitable).toBe(false) + expect(result.reason).toContain('must have at least one data row') + }) it('should return true for tables with at least one suitable column', () => { const table = createTestTable([ ['Name', 'Age', 'Department'], ['Alice', '30', 'Engineering'], ['Bob', '25', 'Marketing'] - ]); + ]) // 'Age' is numeric, so the table should be suitable - const result = isTableSuitable(table); - expect(result.isSuitable).toBe(true); - expect(result.reason).toContain('meets all criteria'); - }); + const result = isTableSuitable(table) + expect(result.isSuitable).toBe(true) + expect(result.reason).toContain('meets all criteria') + }) it('should return true for tables with multiple suitable columns', () => { const table = createTestTable([ ['Name', 'Age', 'Score', 'Active'], ['Alice', '30', '95.5', 'Yes'], ['Bob', '25', '88.0', 'No'] - ]); + ]) - const result = isTableSuitable(table); - expect(result.isSuitable).toBe(true); - expect(result.reason).toContain('meets all criteria'); - }); - }); + const result = isTableSuitable(table) + expect(result.isSuitable).toBe(true) + expect(result.reason).toContain('meets all criteria') + }) + }) describe('findSuitableTables', () => { it('should find and analyze all tables in the document', () => { // Create a test container - const container = document.createElement('div'); - document.body.appendChild(container); + const container = document.createElement('div') + document.body.appendChild(container) // Add a suitable table const table1 = createTestTable([ @@ -98,16 +98,16 @@ describe('Table Detection', () => { ['Alice', '30', '95.5'], ['Bob', '25', '88.0'], ['Charlie', '15', '38.0'] - ]); - container.appendChild(table1); + ]) + container.appendChild(table1) // Add an unsuitable table (only one suitable column) const table2 = createTestTable([ ['Name', 'Department'], ['Alice', 'Engineering'], ['Bob', 'Marketing'] - ]); - container.appendChild(table2); + ]) + container.appendChild(table2) // Add another suitable table const table3 = createTestTable([ @@ -115,24 +115,24 @@ describe('Table Detection', () => { ['Tokyo', '13960000', '2191'], ['New York', '8419000', '783.8'], ['London', '8900000', '1572'] - ]); - container.appendChild(table3); + ]) + container.appendChild(table3) - const results = findSuitableTables(); + const results = findSuitableTables() // Should find 3 tables total - expect(results.length).toBe(3); + expect(results.length).toBe(3) // First table should be suitable - expect(results[0].isSuitable).toBe(true); - expect(results[0].columnTypes).toEqual(['categorical', 'numeric', 'numeric']); + expect(results[0].isSuitable).toBe(true) + expect(results[0].columnTypes).toEqual(['categorical', 'numeric', 'numeric']) // Second table should not be suitable - expect(results[1].isSuitable).toBe(false); + expect(results[1].isSuitable).toBe(false) // Third table should be suitable - expect(results[2].isSuitable).toBe(true); - expect(results[2].columnTypes).toEqual(['categorical', 'numeric', 'numeric']); - }); - }); -}); + expect(results[2].isSuitable).toBe(true) + expect(results[2].columnTypes).toEqual(['categorical', 'numeric', 'numeric']) + }) + }) +}) diff --git a/src/core/__tests__/type-detection.test.ts b/src/core/__tests__/type-detection.test.ts index 84cc4f7..763c26d 100644 --- a/src/core/__tests__/type-detection.test.ts +++ b/src/core/__tests__/type-detection.test.ts @@ -1,15 +1,15 @@ -import { describe, it, expect } from 'vitest'; +import { describe, it, expect } from 'vitest' import { isNumericColumn, isCategoricalColumn, detectColumnTypes, analyzeTable, - extractTableData, -} from '../type-detection'; + extractTableData +} from '../type-detection' import type { ColumnType, TypeDetectionOptions -} from '../type-detection'; +} from '../type-detection' describe('Type Detection', () => { describe('isNumericColumn', () => { @@ -18,44 +18,44 @@ describe('Type Detection', () => { 'Price', '100', '200', - '300', - ]; + '300' + ] - expect(isNumericColumn(values)).toBe(true); - }); + expect(isNumericColumn(values)).toBe(true) + }) it('should identify numeric columns with currency symbols', () => { const values = [ 'Price', '$100', '€200.50', - '£300.75', - ]; + '£300.75' + ] - expect(isNumericColumn(values)).toBe(true); - }); + expect(isNumericColumn(values)).toBe(true) + }) it('should identify numeric columns with commas and periods', () => { const values = [ 'Price', '1,000.50', '2,000.75', - '3,000.25', - ]; + '3,000.25' + ] - expect(isNumericColumn(values)).toBe(true); - }); + expect(isNumericColumn(values)).toBe(true) + }) it('should return false for non-numeric columns', () => { const values = [ 'Name', 'Alice', 'Bob', - 'Charlie', - ]; + 'Charlie' + ] - expect(isNumericColumn(values)).toBe(false); - }); + expect(isNumericColumn(values)).toBe(false) + }) it('should handle empty cells', () => { const values = [ @@ -63,34 +63,34 @@ describe('Type Detection', () => { '', '100', '', - '200', - ]; + '200' + ] // Column is numeric because all non-empty cells are numeric - expect(isNumericColumn(values)).toBe(true); - }); + expect(isNumericColumn(values)).toBe(true) + }) it('should return false if any non-empty cell is not numeric', () => { const values = [ 'Value', '100', 'not a number', - '200', - ]; + '200' + ] - expect(isNumericColumn(values)).toBe(false); - }); + expect(isNumericColumn(values)).toBe(false) + }) it('should work without header row when hasHeader is false', () => { const values = [ '100', '200', - '300', - ]; + '300' + ] - expect(isNumericColumn(values, { hasHeader: false })).toBe(true); - }); - }); + expect(isNumericColumn(values, { hasHeader: false })).toBe(true) + }) + }) describe('isCategoricalColumn', () => { it('should identify categorical columns with enough unique values', () => { @@ -99,11 +99,11 @@ describe('Type Detection', () => { 'A', 'B', 'C', - 'A', - ]; + 'A' + ] - expect(isCategoricalColumn(values)).toBe(true); - }); + expect(isCategoricalColumn(values)).toBe(true) + }) it('should be case-insensitive for categorical values', () => { const values = [ @@ -111,13 +111,13 @@ describe('Type Detection', () => { 'active', 'ACTIVE', 'inactive', - 'INACTIVE', - ]; + 'INACTIVE' + ] // Only 2 unique values when case is ignored - expect(isCategoricalColumn(values, { minUniqueValuesForCategorical: 3 })).toBe(false); - expect(isCategoricalColumn(values, { minUniqueValuesForCategorical: 2 })).toBe(true); - }); + expect(isCategoricalColumn(values, { minUniqueValuesForCategorical: 3 })).toBe(false) + expect(isCategoricalColumn(values, { minUniqueValuesForCategorical: 2 })).toBe(true) + }) it('should return false for columns with too few unique values', () => { const values = [ @@ -125,15 +125,15 @@ describe('Type Detection', () => { 'Yes', 'No', 'Yes', - 'No', - ]; + 'No' + ] // Only 2 unique values, which is below the default threshold of 3 - expect(isCategoricalColumn(values)).toBe(false); + expect(isCategoricalColumn(values)).toBe(false) // Should pass with lower threshold - expect(isCategoricalColumn(values, { minUniqueValuesForCategorical: 2 })).toBe(true); - }); + expect(isCategoricalColumn(values, { minUniqueValuesForCategorical: 2 })).toBe(true) + }) it('should ignore empty cells', () => { const values = [ @@ -141,27 +141,27 @@ describe('Type Detection', () => { '', 'A', 'B', - 'C', - ]; + 'C' + ] // 3 unique non-empty values - expect(isCategoricalColumn(values, { minUniqueValuesForCategorical: 3 })).toBe(true); - }); + expect(isCategoricalColumn(values, { minUniqueValuesForCategorical: 3 })).toBe(true) + }) it('should work without header row when hasHeader is false', () => { const values = [ 'A', 'B', 'C', - 'A', - ]; + 'A' + ] expect(isCategoricalColumn(values, { hasHeader: false, minUniqueValuesForCategorical: 2 - })).toBe(true); - }); - }); + })).toBe(true) + }) + }) describe('detectColumnTypes', () => { it('should detect column types correctly', () => { @@ -169,36 +169,36 @@ describe('Type Detection', () => { ['Name', 'Age', 'Score', 'Active'], ['Alice', '30', '95.5', 'Yes'], ['Bob', '25', '88.0', 'No'], - ['Charlie', '35', '92.3', 'Yes'], - ]; + ['Charlie', '35', '92.3', 'Yes'] + ] - const expected: ColumnType[] = ['categorical', 'numeric', 'numeric', 'unknown']; - expect(detectColumnTypes(rows)).toEqual(expected); - }); + const expected: ColumnType[] = ['categorical', 'numeric', 'numeric', 'unknown'] + expect(detectColumnTypes(rows)).toEqual(expected) + }) it('should handle mixed content columns', () => { const rows = [ ['ID', 'Value'], ['1', '100'], ['2', 'Not a number'], - ['3', '200'], - ]; + ['3', '200'] + ] // Value column is not numeric because it contains non-numeric values - expect(detectColumnTypes(rows)).toEqual(['numeric', 'unknown']); - }); + expect(detectColumnTypes(rows)).toEqual(['numeric', 'unknown']) + }) it('should handle tables without headers', () => { const rows = [ ['Alice', '30', '95.5'], ['Bob', '25', '88.0'], - ['Charlie', '35', '92.3'], - ]; + ['Charlie', '35', '92.3'] + ] - const options: TypeDetectionOptions = { hasHeader: false }; - expect(detectColumnTypes(rows, options)).toEqual(['categorical', 'numeric', 'numeric']); - }); - }); + const options: TypeDetectionOptions = { hasHeader: false } + expect(detectColumnTypes(rows, options)).toEqual(['categorical', 'numeric', 'numeric']) + }) + }) describe('analyzeTable', () => { it('should mark table as suitable with multiple numeric/categorical columns', () => { @@ -206,63 +206,63 @@ describe('Type Detection', () => { ['Name', 'Age', 'Score', 'Active'], ['Alice', '30', '95.5', 'Yes'], ['Bob', '25', '88.0', 'No'], - ['Charlie', '36', '75.5', 'No'], - ]; + ['Charlie', '36', '75.5', 'No'] + ] - const result = analyzeTable(rows); - expect(result.isSuitable).toBe(true); - expect(result.columnTypes).toEqual(['categorical', 'numeric', 'numeric', 'unknown']); - }); + const result = analyzeTable(rows) + expect(result.isSuitable).toBe(true) + expect(result.columnTypes).toEqual(['categorical', 'numeric', 'numeric', 'unknown']) + }) it('should mark table as suitable with multiple numeric/categorical column', () => { const rows = [ ['ID', 'Name', 'Value'], ['1', 'Alice', '100'], - ['2', 'Bob', '200'], - ]; + ['2', 'Bob', '200'] + ] - const result = analyzeTable(rows); - expect(result.isSuitable).toBe(true); + const result = analyzeTable(rows) + expect(result.isSuitable).toBe(true) // First column is numeric because '1' and '2' are valid numbers // Second column is unknown because 'Alice' and 'Bob' are too few terms // Third column is numeric because '100' and '200' are valid numbers - expect(result.columnTypes).toEqual(['numeric', 'unknown', 'numeric']); - }); - }); + expect(result.columnTypes).toEqual(['numeric', 'unknown', 'numeric']) + }) + }) describe('extractTableData', () => { it('should extract table data into a 2D string array', () => { - const table = document.createElement('table'); + const table = document.createElement('table') - const headerRow = table.insertRow(); - headerRow.insertCell().textContent = 'Name'; - headerRow.insertCell().textContent = 'Age'; + const headerRow = table.insertRow() + headerRow.insertCell().textContent = 'Name' + headerRow.insertCell().textContent = 'Age' - const dataRow1 = table.insertRow(); - dataRow1.insertCell().textContent = 'Alice'; - dataRow1.insertCell().textContent = '30'; + const dataRow1 = table.insertRow() + dataRow1.insertCell().textContent = 'Alice' + dataRow1.insertCell().textContent = '30' - const dataRow2 = table.insertRow(); - dataRow2.insertCell().textContent = 'Bob'; - dataRow2.insertCell().textContent = '25'; + const dataRow2 = table.insertRow() + dataRow2.insertCell().textContent = 'Bob' + dataRow2.insertCell().textContent = '25' - const result = extractTableData(table); + const result = extractTableData(table) expect(result).toEqual([ ['Name', 'Age'], ['Alice', '30'], ['Bob', '25'] - ]); - }); + ]) + }) it('should handle empty cells', () => { - const table = document.createElement('table'); - const row = table.insertRow(); - row.insertCell().textContent = 'A'; - row.insertCell(); // Empty cell - row.insertCell().textContent = 'C'; + const table = document.createElement('table') + const row = table.insertRow() + row.insertCell().textContent = 'A' + row.insertCell() // Empty cell + row.insertCell().textContent = 'C' - const result = extractTableData(table); - expect(result).toEqual([['A', '', 'C']]); - }); - }); -}); + const result = extractTableData(table) + expect(result).toEqual([['A', '', 'C']]) + }) + }) +}) diff --git a/src/core/table-detection.ts b/src/core/table-detection.ts index 9e8b342..10b8de8 100644 --- a/src/core/table-detection.ts +++ b/src/core/table-detection.ts @@ -1,16 +1,15 @@ -import { analyzeTable as analyzeTableType, extractTableData } from './type-detection'; -import type { ColumnType } from './type-detection'; +import { analyzeTable as analyzeTableType, extractTableData } from './type-detection' +import type { ColumnType } from './type-detection' // Re-export analyzeTable from type-detection -export { analyzeTableType as analyzeTable }; - +export { analyzeTableType as analyzeTable } /** * Finds all table elements in the document. * @returns A NodeListOf containing all tables found. */ export function findTables(): NodeListOf { - return document.querySelectorAll('table'); + return document.querySelectorAll('table') } /** @@ -19,7 +18,7 @@ export function findTables(): NodeListOf { * @returns True if the table has both thead and tbody, false otherwise. */ function hasValidTableStructure(table: HTMLTableElement): boolean { - return table.tHead !== null && table.tBodies.length > 0; + return table.tHead !== null && table.tBodies.length > 0 } /** @@ -40,7 +39,7 @@ export function isTableSuitable(table: HTMLTableElement): { isSuitable: boolean; return { isSuitable: false, reason: 'Table must have both and elements' - }; + } } // Check for minimum row count (header + at least one data row) @@ -48,29 +47,29 @@ export function isTableSuitable(table: HTMLTableElement): { isSuitable: boolean; return { isSuitable: false, reason: 'Table must have at least one data row' - }; + } } // Extract table data and analyze column types - const tableData = extractTableData(table); - const { columnTypes } = analyzeTableType(tableData); + const tableData = extractTableData(table) + const { columnTypes } = analyzeTableType(tableData) // Check if we have at least one suitable column (numeric or categorical) - const suitableColumns = columnTypes.filter((t) => t === 'numeric' || t === 'categorical'); - const hasEnoughSuitableColumns = suitableColumns.length >= 1; + const suitableColumns = columnTypes.filter((t) => t === 'numeric' || t === 'categorical') + const hasEnoughSuitableColumns = suitableColumns.length >= 1 // Update the reason based on the analysis - let reason = ''; + let reason = '' if (!hasEnoughSuitableColumns) { - reason = 'Table needs at least one numeric or categorical column'; + reason = 'Table needs at least one numeric or categorical column' } else { - reason = 'Table meets all criteria for enrichment'; + reason = 'Table meets all criteria for enrichment' } return { isSuitable: hasEnoughSuitableColumns, reason - }; + } } /** @@ -83,27 +82,27 @@ export function findSuitableTables(): Array<{ reason: string; columnTypes: ColumnType[]; }> { - const tables = findTables(); - return Array.from(tables).map(table => { - const tableData = extractTableData(table); - const { columnTypes, isSuitable } = analyzeTableType(tableData); + const tables = findTables() + return Array.from(tables).map((table) => { + const tableData = extractTableData(table) + const { columnTypes, isSuitable } = analyzeTableType(tableData) // Check if we have at least one suitable column - const suitableColumns = columnTypes.filter((t: ColumnType) => t === 'numeric' || t === 'categorical'); - const hasEnoughSuitableColumns = suitableColumns.length >= 1; + const suitableColumns = columnTypes.filter((t: ColumnType) => t === 'numeric' || t === 'categorical') + const hasEnoughSuitableColumns = suitableColumns.length >= 1 // Get the reason for suitability - let reason = ''; + let reason = '' if (!table.tHead || table.tBodies.length === 0) { - reason = 'Table must have both and elements'; + reason = 'Table must have both and elements' } else if (table.rows.length < 2) { - reason = 'Table must have at least one data row'; + reason = 'Table must have at least one data row' } else if (!hasEnoughSuitableColumns) { - reason = 'Table does not have enough suitable columns (need at least 1)'; + reason = 'Table does not have enough suitable columns (need at least 1)' } else { - reason = 'Table meets all criteria for enrichment'; + reason = 'Table meets all criteria for enrichment' } - return { table, isSuitable, reason, columnTypes }; - }); + return { table, isSuitable, reason, columnTypes } + }) } diff --git a/src/core/table-processor.ts b/src/core/table-processor.ts index ac8d31d..129ac62 100644 --- a/src/core/table-processor.ts +++ b/src/core/table-processor.ts @@ -30,34 +30,34 @@ export function processTable( id = `table-${Math.random().toString(36).substr(2, 9)}`, applyStyles = true, onComplete - } = options; + } = options // Ensure the table has an ID if (!table.id) { - table.id = id; + table.id = id } try { // Add a data attribute to mark the table as processed - table.dataset.gridSightProcessed = 'true'; + table.dataset.gridSightProcessed = 'true' // Apply default styles if enabled if (applyStyles) { - applyTableStyles(table); + applyTableStyles(table) } // Process table data - processTableData(table); + processTableData(table) // Call the completion callback if provided if (typeof onComplete === 'function') { - onComplete(table); + onComplete(table) } - return table; + return table } catch (error) { - console.error('Error processing table:', error); - throw error; + console.error('Error processing table:', error) + throw error } } @@ -68,12 +68,12 @@ export function processTable( function processTableData(table: HTMLTableElement): void { // This is a placeholder for future table processing logic // For now, we'll just add a data attribute to each cell - const cells = table.querySelectorAll('td, th'); + const cells = table.querySelectorAll('td, th') cells.forEach((cell, index) => { if (!cell.hasAttribute('data-gs-cell-index')) { - cell.setAttribute('data-gs-cell-index', index.toString()); + cell.setAttribute('data-gs-cell-index', index.toString()) } - }); + }) } /** @@ -82,12 +82,12 @@ function processTableData(table: HTMLTableElement): void { */ function applyTableStyles(table: HTMLTableElement): void { // Add a class to the table for styling - table.classList.add('grid-sight-table'); + table.classList.add('grid-sight-table') // Add some basic default styles if no styles are already applied if (!document.querySelector('style[data-grid-sight-styles]')) { - const style = document.createElement('style'); - style.setAttribute('data-grid-sight-styles', 'true'); + const style = document.createElement('style') + style.setAttribute('data-grid-sight-styles', 'true') style.textContent = ` .grid-sight-table { border-collapse: collapse; @@ -111,8 +111,8 @@ function applyTableStyles(table: HTMLTableElement): void { .grid-sight-table tr:hover { background-color: #f1f1f1; } - `; - document.head.appendChild(style); + ` + document.head.appendChild(style) } } @@ -124,5 +124,5 @@ export function isValidTable(table: HTMLTableElement): boolean { return table && table instanceof HTMLTableElement && table.tHead !== null && - table.tBodies.length > 0; + table.tBodies.length > 0 } diff --git a/src/core/type-detection.ts b/src/core/type-detection.ts index dd4a394..7a67743 100644 --- a/src/core/type-detection.ts +++ b/src/core/type-detection.ts @@ -12,8 +12,8 @@ export interface TypeDetectionOptions { const DEFAULT_OPTIONS: Required = { minUniqueValuesForCategorical: 3, numericThreshold: 0.8, // 80% of cells must be numeric to consider column numeric - hasHeader: true, -}; + hasHeader: true +} /** * Cleans a string value to extract a numeric value, handling various number formats and currency symbols @@ -21,65 +21,65 @@ const DEFAULT_OPTIONS: Required = { * @returns The cleaned number as a float, or null if the value cannot be converted to a number */ export function cleanNumericCell(value: string | number | null | undefined): number | null { - if (value === null || value === undefined) return null; + if (value === null || value === undefined) return null // If it's already a number, return it directly - if (typeof value === 'number') return isFinite(value) ? value : null; + if (typeof value === 'number') return isFinite(value) ? value : null // Handle empty strings - const trimmed = value.trim(); - if (!trimmed) return null; + const trimmed = value.trim() + if (!trimmed) return null // Handle currency symbols and other non-numeric prefixes/suffixes // Remove any non-numeric characters except digits, decimal points, commas, and minus let numericString = trimmed // Remove all non-numeric characters except digits, decimal points, commas, and minus - .replace(/[^\d.,-]/g, ''); + .replace(/[^\d.,-]/g, '') // If we're left with an empty string, it wasn't a valid number - if (!numericString) return null; + if (!numericString) return null // Handle negative numbers (only allow minus at the start) - const isNegative = numericString.startsWith('-'); + const isNegative = numericString.startsWith('-') if (isNegative) { - numericString = numericString.slice(1); + numericString = numericString.slice(1) } // Remove any remaining minus signs (they're only valid at the start) - if (numericString.includes('-')) return null; + if (numericString.includes('-')) return null // Handle decimal separators - only allow one decimal point - const parts = numericString.split('.'); - if (parts.length > 2) return null; // More than one decimal point + const parts = numericString.split('.') + if (parts.length > 2) return null // More than one decimal point // Handle thousands separators (commas) - they must be in the correct positions if (parts[0].includes(',')) { // Check that commas are only used as thousand separators - const integerPart = parts[0]; - const groups = integerPart.split(','); + const integerPart = parts[0] + const groups = integerPart.split(',') // First group can be 1-3 digits, subsequent groups must be exactly 3 digits - if (groups[0].length === 0 || groups[0].length > 3) return null; + if (groups[0].length === 0 || groups[0].length > 3) return null for (let i = 1; i < groups.length; i++) { - if (groups[i].length !== 3) return null; + if (groups[i].length !== 3) return null } // Remove commas for final parsing - numericString = groups.join('') + (parts[1] ? `.${parts[1]}` : ''); + numericString = groups.join('') + (parts[1] ? `.${parts[1]}` : '') } else if (parts.length === 2) { // No commas, but has a decimal point - ensure decimal part is valid - if (parts[1].includes(',')) return null; // Comma in decimal part - numericString = parts[0] + '.' + parts[1]; + if (parts[1].includes(',')) return null // Comma in decimal part + numericString = parts[0] + '.' + parts[1] } // Re-add the negative sign if it was present if (isNegative) { - numericString = '-' + numericString; + numericString = '-' + numericString } // Parse the final number - const num = parseFloat(numericString); - return isFinite(num) ? num : null; + const num = parseFloat(numericString) + return isFinite(num) ? num : null } /** @@ -88,7 +88,7 @@ export function cleanNumericCell(value: string | number | null | undefined): num * @returns true if the string can be converted to a number, false otherwise */ function isNumericValue(value: string): boolean { - return cleanNumericCell(value) !== null; + return cleanNumericCell(value) !== null } /** @@ -101,21 +101,21 @@ export function isNumericColumn( values: string[], options: TypeDetectionOptions = {} ): boolean { - const { hasHeader } = { ...DEFAULT_OPTIONS, ...options }; + const { hasHeader } = { ...DEFAULT_OPTIONS, ...options } // Skip header if present - const dataValues = hasHeader ? values.slice(1) : [...values]; + const dataValues = hasHeader ? values.slice(1) : [...values] // Get non-empty values const nonEmptyValues = dataValues - .map(value => value?.trim()) - .filter((value): value is string => !!value); + .map((value) => value?.trim()) + .filter((value): value is string => !!value) // If all values are empty, it's not numeric - if (nonEmptyValues.length === 0) return false; + if (nonEmptyValues.length === 0) return false // All non-empty values must be numeric - return nonEmptyValues.every(value => isNumericValue(value)); + return nonEmptyValues.every((value) => isNumericValue(value)) } /** @@ -129,31 +129,31 @@ export function isCategoricalColumn( values: string[], options: TypeDetectionOptions = {} ): boolean { - const { minUniqueValuesForCategorical, hasHeader } = { ...DEFAULT_OPTIONS, ...options }; + const { minUniqueValuesForCategorical, hasHeader } = { ...DEFAULT_OPTIONS, ...options } // Skip header if present - const dataValues = hasHeader ? values.slice(1) : [...values]; + const dataValues = hasHeader ? values.slice(1) : [...values] // Get non-empty values const nonEmptyValues = dataValues - .map(value => value?.trim()) - .filter((value): value is string => !!value); + .map((value) => value?.trim()) + .filter((value): value is string => !!value) // If all values are empty, it's not categorical - if (nonEmptyValues.length === 0) return false; + if (nonEmptyValues.length === 0) return false // Check that no values are numeric if (nonEmptyValues.some(isNumericValue)) { - return false; + return false } // Count unique values (case insensitive) const uniqueValues = new Set( - nonEmptyValues.map(value => value.toLowerCase()) - ); + nonEmptyValues.map((value) => value.toLowerCase()) + ) // Must have at least the minimum number of unique values - return uniqueValues.size >= minUniqueValuesForCategorical; + return uniqueValues.size >= minUniqueValuesForCategorical } /** @@ -167,29 +167,29 @@ export function detectColumnTypes( rows: string[][], options: TypeDetectionOptions = {} ): ColumnType[] { - if (!rows.length) return []; + if (!rows.length) return [] - const columnCount = rows[0].length; - const columnTypes: ColumnType[] = []; + const columnCount = rows[0].length + const columnTypes: ColumnType[] = [] for (let col = 0; col < columnCount; col++) { - const column = rows.map(row => row[col] || ''); + const column = rows.map((row) => row[col] || '') // First check if it's numeric (most restrictive) if (isNumericColumn(column, options)) { - columnTypes.push('numeric'); + columnTypes.push('numeric') } // Then check if it's categorical else if (isCategoricalColumn(column, options)) { - columnTypes.push('categorical'); + columnTypes.push('categorical') } // Default to unknown else { - columnTypes.push('unknown'); + columnTypes.push('unknown') } } - return columnTypes; + return columnTypes } /** @@ -199,37 +199,37 @@ export function analyzeTable( rows: string[][], options: TypeDetectionOptions = {} ): { columnTypes: ColumnType[]; isSuitable: boolean } { - const columnTypes = detectColumnTypes(rows, options); + const columnTypes = detectColumnTypes(rows, options) // Table is suitable if it has at least 2 columns with numeric or categorical data const suitableColumnCount = columnTypes.filter( - type => type === 'numeric' || type === 'categorical' - ).length; + (type) => type === 'numeric' || type === 'categorical' + ).length return { columnTypes, - isSuitable: suitableColumnCount >= 1, - }; + isSuitable: suitableColumnCount >= 1 + } } /** * Extracts text content from an HTML table into a 2D array of strings */ export function extractTableData(table: HTMLTableElement): string[][] { - if (!table.rows) return []; + if (!table.rows) return [] - const rows: string[][] = []; + const rows: string[][] = [] for (let i = 0; i < table.rows.length; i++) { - const row = table.rows[i]; - const rowData: string[] = []; + const row = table.rows[i] + const rowData: string[] = [] for (let j = 0; j < row.cells.length; j++) { - rowData.push(row.cells[j]?.textContent?.trim() || ''); + rowData.push(row.cells[j]?.textContent?.trim() || '') } - rows.push(rowData); + rows.push(rowData) } - return rows; + return rows } diff --git a/src/enrichments/__tests__/heatmap-utils.test.ts b/src/enrichments/__tests__/heatmap-utils.test.ts index 744ab56..e82450c 100644 --- a/src/enrichments/__tests__/heatmap-utils.test.ts +++ b/src/enrichments/__tests__/heatmap-utils.test.ts @@ -1,148 +1,148 @@ -import { describe, it, expect, beforeEach } from 'vitest'; +import { describe, it, expect, beforeEach } from 'vitest' import { getColorForValue, normalizeValue, calculateMinMax, extractNumericValues -} from '../heatmap-utils'; +} from '../heatmap-utils' // Mock DOM elements for extractNumericValues tests const createMockTable = (rows: string[][]) => { - const table = document.createElement('table'); - const tbody = document.createElement('tbody'); + const table = document.createElement('table') + const tbody = document.createElement('tbody') - rows.forEach(rowData => { - const row = document.createElement('tr'); - rowData.forEach(cellText => { - const cell = document.createElement('td'); - cell.textContent = cellText; - row.appendChild(cell); - }); - tbody.appendChild(row); - }); + rows.forEach((rowData) => { + const row = document.createElement('tr') + rowData.forEach((cellText) => { + const cell = document.createElement('td') + cell.textContent = cellText + row.appendChild(cell) + }) + tbody.appendChild(row) + }) - table.appendChild(tbody); - return table; -}; + table.appendChild(tbody) + return table +} describe('Heatmap Utils', () => { describe('normalizeValue', () => { it('should normalize a value within range', () => { - expect(normalizeValue(5, 0, 10)).toBe(0.5); - expect(normalizeValue(2, 0, 10)).toBe(0.2); - expect(normalizeValue(8, 0, 10)).toBe(0.8); - }); + expect(normalizeValue(5, 0, 10)).toBe(0.5) + expect(normalizeValue(2, 0, 10)).toBe(0.2) + expect(normalizeValue(8, 0, 10)).toBe(0.8) + }) it('should handle min and max values', () => { - expect(normalizeValue(0, 0, 10)).toBe(0); - expect(normalizeValue(10, 0, 10)).toBe(1); - }); + expect(normalizeValue(0, 0, 10)).toBe(0) + expect(normalizeValue(10, 0, 10)).toBe(1) + }) it('should handle values outside range', () => { - expect(normalizeValue(-5, 0, 10)).toBe(0); - expect(normalizeValue(15, 0, 10)).toBe(1); - }); + expect(normalizeValue(-5, 0, 10)).toBe(0) + expect(normalizeValue(15, 0, 10)).toBe(1) + }) it('should handle zero range', () => { - expect(normalizeValue(5, 5, 5)).toBe(0); - }); - }); + expect(normalizeValue(5, 5, 5)).toBe(0) + }) + }) describe('getColorForValue', () => { - const colorScale = ['#000000', '#888888', '#FFFFFF']; + const colorScale = ['#000000', '#888888', '#FFFFFF'] it('should return first color for min value', () => { - expect(getColorForValue(0, 0, 1, colorScale)).toBe(colorScale[0]); - }); + expect(getColorForValue(0, 0, 1, colorScale)).toBe(colorScale[0]) + }) it('should return last color for max value', () => { - expect(getColorForValue(1, 0, 1, colorScale)).toBe(colorScale[2]); - }); + expect(getColorForValue(1, 0, 1, colorScale)).toBe(colorScale[2]) + }) it('should return middle color for middle value', () => { - expect(getColorForValue(0.5, 0, 1, colorScale)).toBe(colorScale[1]); - }); + expect(getColorForValue(0.5, 0, 1, colorScale)).toBe(colorScale[1]) + }) it('should handle custom min/max values', () => { - expect(getColorForValue(50, 0, 100, colorScale)).toBe(colorScale[1]); - }); + expect(getColorForValue(50, 0, 100, colorScale)).toBe(colorScale[1]) + }) it('should handle values outside range', () => { - expect(getColorForValue(-10, 0, 10, colorScale)).toBe(colorScale[0]); - expect(getColorForValue(20, 0, 10, colorScale)).toBe(colorScale[2]); - }); - }); + expect(getColorForValue(-10, 0, 10, colorScale)).toBe(colorScale[0]) + expect(getColorForValue(20, 0, 10, colorScale)).toBe(colorScale[2]) + }) + }) describe('calculateMinMax', () => { it('should calculate min and max for numeric arrays', () => { - const values = [1, 5, 3, 10, 2]; - const { min, max } = calculateMinMax(values); - expect(min).toBe(1); - expect(max).toBe(10); - }); + const values = [1, 5, 3, 10, 2] + const { min, max } = calculateMinMax(values) + expect(min).toBe(1) + expect(max).toBe(10) + }) it('should handle string numbers', () => { - const values = ['1', '5', '3', '10', '2']; - const { min, max } = calculateMinMax(values); - expect(min).toBe(1); - expect(max).toBe(10); - }); + const values = ['1', '5', '3', '10', '2'] + const { min, max } = calculateMinMax(values) + expect(min).toBe(1) + expect(max).toBe(10) + }) it('should ignore non-numeric values', () => { - const values = [1, 'not a number', 5, '3', '']; - const { min, max } = calculateMinMax(values); - expect(min).toBe(1); - expect(max).toBe(5); - }); + const values = [1, 'not a number', 5, '3', ''] + const { min, max } = calculateMinMax(values) + expect(min).toBe(1) + expect(max).toBe(5) + }) it('should handle arrays with null or undefined', () => { - const values = [1, 5, null as unknown as string, undefined as unknown as string]; - const { min, max } = calculateMinMax(values); - expect(min).toBe(1); - expect(max).toBe(5); - }); + const values = [1, 5, null as unknown as string, undefined as unknown as string] + const { min, max } = calculateMinMax(values) + expect(min).toBe(1) + expect(max).toBe(5) + }) it('should return 0 for empty arrays', () => { - const { min, max } = calculateMinMax([]); - expect(min).toBe(0); - expect(max).toBe(0); - }); + const { min, max } = calculateMinMax([]) + expect(min).toBe(0) + expect(max).toBe(0) + }) it('should use provided min/max values', () => { - const values = [1, 2, 3]; - const { min, max } = calculateMinMax(values, 0, 10); - expect(min).toBe(0); - expect(max).toBe(10); - }); - }); + const values = [1, 2, 3] + const { min, max } = calculateMinMax(values, 0, 10) + expect(min).toBe(0) + expect(max).toBe(10) + }) + }) describe('extractNumericValues', () => { - let table: HTMLTableElement; + let table: HTMLTableElement beforeEach(() => { table = createMockTable([ ['Header', '1', '2.5', '3'], ['Row 1', '4', '5.5', '6'], ['Row 2', '7', 'not a number', '9'] - ]); - }); + ]) + }) it('should extract numeric values from table cells', () => { - const cells = Array.from(table.querySelectorAll('td')); - const values = extractNumericValues(cells); - expect(values).toEqual([1, 2.5, 3, 4, 5.5, 6, 7, 9]); - }); + const cells = Array.from(table.querySelectorAll('td')) + const values = extractNumericValues(cells) + expect(values).toEqual([1, 2.5, 3, 4, 5.5, 6, 7, 9]) + }) it('should skip header row when skipHeader is true', () => { - const cells = Array.from(table.querySelectorAll('td')); - const values = extractNumericValues(cells, { skipHeader: true }); - expect(values).toEqual([4, 5.5, 6, 7, 9]); - }); + const cells = Array.from(table.querySelectorAll('td')) + const values = extractNumericValues(cells, { skipHeader: true }) + expect(values).toEqual([4, 5.5, 6, 7, 9]) + }) it('should handle empty cells', () => { - const emptyCell = document.createElement('td'); - const values = extractNumericValues([emptyCell]); - expect(values).toEqual([]); - }); - }); -}); + const emptyCell = document.createElement('td') + const values = extractNumericValues([emptyCell]) + expect(values).toEqual([]) + }) + }) +}) diff --git a/src/enrichments/__tests__/heatmap.test.ts b/src/enrichments/__tests__/heatmap.test.ts index 2e94296..f3d395b 100644 --- a/src/enrichments/__tests__/heatmap.test.ts +++ b/src/enrichments/__tests__/heatmap.test.ts @@ -1,10 +1,10 @@ -import { toggleHeatmap, removeHeatmap, applyHeatmap, isHeatmapActive } from '../heatmap'; -import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import { toggleHeatmap, removeHeatmap, applyHeatmap, isHeatmapActive } from '../heatmap' +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' describe('Heatmap', () => { // Create a test table with numeric data const createTestTable = (): HTMLTableElement => { - const table = document.createElement('table'); + const table = document.createElement('table') table.innerHTML = ` @@ -28,268 +28,268 @@ describe('Heatmap', () => { 35 - `; - document.body.appendChild(table); - return table; - }; + ` + document.body.appendChild(table) + return table + } // Helper function to get a cell by test ID const getCell = (table: HTMLTableElement, row: number, col: number): HTMLElement => { // First try to find by test ID - const cellByTestId = table.querySelector(`[data-testid="cell-${row}-${col}"]`) as HTMLElement; - if (cellByTestId) return cellByTestId; + const cellByTestId = table.querySelector(`[data-testid="cell-${row}-${col}"]`) as HTMLElement + if (cellByTestId) return cellByTestId // Fall back to row/column indices if test ID not found - const rowEl = table.rows[row]; - if (!rowEl) throw new Error(`Row ${row} not found`); + const rowEl = table.rows[row] + if (!rowEl) throw new Error(`Row ${row} not found`) - const cell = rowEl.cells[col]; - if (!cell) throw new Error(`Cell at row ${row}, col ${col} not found`); + const cell = rowEl.cells[col] + if (!cell) throw new Error(`Cell at row ${row}, col ${col} not found`) - return cell as HTMLElement; - }; + return cell as HTMLElement + } // We can use isHeatmapActive directly from the import afterEach(() => { // Clean up after each test - document.body.innerHTML = ''; - vi.restoreAllMocks(); - }); + document.body.innerHTML = '' + vi.restoreAllMocks() + }) beforeEach(() => { // Mock console.warn to track warnings - vi.spyOn(console, 'warn').mockImplementation(() => {}); - }); + vi.spyOn(console, 'warn').mockImplementation(() => {}) + }) describe('Column Heatmap', () => { it('should apply heatmap to a column', () => { // Arrange - const table = createTestTable(); - const columnIndex = 1; // First data column + const table = createTestTable() + const columnIndex = 1 // First data column // Act - apply heatmap to the first data column - toggleHeatmap(table, columnIndex, 'column'); + toggleHeatmap(table, columnIndex, 'column') // Assert // 1. Check if heatmap is active for this column // Check if heatmap is active for this column - expect(isHeatmapActive(table, columnIndex, 'column')).toBe(true); + expect(isHeatmapActive(table, columnIndex, 'column')).toBe(true) // 2. Check if the table has the heatmap class - expect(table.classList.contains('gs-heatmap')).toBe(true); + expect(table.classList.contains('gs-heatmap')).toBe(true) // 3. Check if cells in the column have background colors // Note: Adjust indices based on actual table structure - const firstDataCell = table.querySelector('tbody td') as HTMLElement; - expect(firstDataCell).toBeTruthy(); + const firstDataCell = table.querySelector('tbody td') as HTMLElement + expect(firstDataCell).toBeTruthy() // Check that at least one cell has a background color const hasColoredCell = Array.from(table.querySelectorAll('td')).some( - cell => (cell as HTMLElement).style.backgroundColor - ); - expect(hasColoredCell).toBe(true); + (cell) => (cell as HTMLElement).style.backgroundColor + ) + expect(hasColoredCell).toBe(true) // Clean up - removeHeatmap(table); - }); + removeHeatmap(table) + }) it('should remove heatmap when toggled off', () => { // Arrange - const table = createTestTable(); - const columnIndex = 1; + const table = createTestTable() + const columnIndex = 1 // Act - toggle heatmap on and off - toggleHeatmap(table, columnIndex, 'column'); + toggleHeatmap(table, columnIndex, 'column') // Apply heatmap and verify it's applied before toggling off - toggleHeatmap(table, columnIndex, 'column'); + toggleHeatmap(table, columnIndex, 'column') // Assert - check if heatmap is removed // The table should no longer have the heatmap class - expect(table.classList.contains('gs-heatmap')).toBe(false); + expect(table.classList.contains('gs-heatmap')).toBe(false) // Cells should no longer have background colors const hasColoredCell = Array.from(table.querySelectorAll('td')).some( - cell => (cell as HTMLElement).style.backgroundColor - ); - expect(hasColoredCell).toBe(false); - }); + (cell) => (cell as HTMLElement).style.backgroundColor + ) + expect(hasColoredCell).toBe(false) + }) it('should remove heatmap when toggling off', () => { // Arrange - const table = createTestTable(); - const columnIndex = 1; + const table = createTestTable() + const columnIndex = 1 // Act - apply heatmap - toggleHeatmap(table, columnIndex, 'column'); + toggleHeatmap(table, columnIndex, 'column') // Get a reference to a cell that should be colored - const cell = table.querySelector('td') as HTMLElement; - expect(cell).toBeTruthy(); - const initialColor = cell.style.backgroundColor; - expect(initialColor).toBeTruthy(); + const cell = table.querySelector('td') as HTMLElement + expect(cell).toBeTruthy() + const initialColor = cell.style.backgroundColor + expect(initialColor).toBeTruthy() // Toggle off - toggleHeatmap(table, columnIndex, 'column'); + toggleHeatmap(table, columnIndex, 'column') // Assert - cell should no longer have a background color - expect(cell.style.backgroundColor).toBe(''); - }); + expect(cell.style.backgroundColor).toBe('') + }) it('should handle overlapping row and column heatmaps with diagonal split', () => { - const table = createTestTable(); - document.body.appendChild(table); + const table = createTestTable() + document.body.appendChild(table) // Apply column heatmap - toggleHeatmap(table, 1, 'column'); + toggleHeatmap(table, 1, 'column') // Get the cell that will be split - const cell = table.querySelector('tbody tr:nth-child(1) td:nth-child(1)') as HTMLElement; - const initialColor = cell.style.backgroundColor; + const cell = table.querySelector('tbody tr:nth-child(1) td:nth-child(1)') as HTMLElement + const initialColor = cell.style.backgroundColor // Apply row heatmap that will overlap with the column - toggleHeatmap(table, 0, 'row'); + toggleHeatmap(table, 0, 'row') // Should have the split class and custom properties - expect(cell.classList.contains('gs-heatmap-split')).toBe(true); - expect(cell.style.getPropertyValue('--split-color-1')).toBeTruthy(); - expect(cell.style.getPropertyValue('--split-color-2')).toBeTruthy(); + expect(cell.classList.contains('gs-heatmap-split')).toBe(true) + expect(cell.style.getPropertyValue('--split-color-1')).toBeTruthy() + expect(cell.style.getPropertyValue('--split-color-2')).toBeTruthy() // Should not have a background color directly set - expect(cell.style.backgroundColor).toBe(''); + expect(cell.style.backgroundColor).toBe('') // Remove the row heatmap - removeHeatmap(table, 0, 'row'); + removeHeatmap(table, 0, 'row') // Should remove the split and restore column heatmap - expect(cell.classList.contains('gs-heatmap-split')).toBe(false); - expect(cell.style.backgroundColor).toBe(initialColor); + expect(cell.classList.contains('gs-heatmap-split')).toBe(false) + expect(cell.style.backgroundColor).toBe(initialColor) // Clean up - removeHeatmap(table); - }); + removeHeatmap(table) + }) it('should handle removing heatmap with overlapping cells', () => { - const table = createTestTable(); - document.body.appendChild(table); + const table = createTestTable() + document.body.appendChild(table) // Apply both row and column heatmaps - toggleHeatmap(table, 1, 'column'); - toggleHeatmap(table, 0, 'row'); + toggleHeatmap(table, 1, 'column') + toggleHeatmap(table, 0, 'row') // Get the cell at the intersection - const cell = table.querySelector('tbody tr:nth-child(1) td:nth-child(2)') as HTMLElement; + const cell = table.querySelector('tbody tr:nth-child(1) td:nth-child(2)') as HTMLElement // Remove the column heatmap - removeHeatmap(table, 1, 'column'); + removeHeatmap(table, 1, 'column') // Should remove the split and restore row heatmap - expect(cell.classList.contains('gs-heatmap-split')).toBe(false); - expect(cell.style.getPropertyValue('--split-color-1')).toBe(''); - expect(cell.style.getPropertyValue('--split-color-2')).toBe(''); - expect(cell.style.backgroundColor).toBeTruthy(); + expect(cell.classList.contains('gs-heatmap-split')).toBe(false) + expect(cell.style.getPropertyValue('--split-color-1')).toBe('') + expect(cell.style.getPropertyValue('--split-color-2')).toBe('') + expect(cell.style.backgroundColor).toBeTruthy() // Clean up - removeHeatmap(table); - }); - }); + removeHeatmap(table) + }) + }) describe('Row Heatmap', () => { it('should apply heatmap to a numeric row', () => { // Arrange - const table = createTestTable(); - const rowIndex = 1; // First data row + const table = createTestTable() + const rowIndex = 1 // First data row // Act - apply heatmap to the first data row - toggleHeatmap(table, rowIndex, 'row'); + toggleHeatmap(table, rowIndex, 'row') // Assert // 1. Check if the table has the heatmap class - expect(table.classList.contains('gs-heatmap')).toBe(true); + expect(table.classList.contains('gs-heatmap')).toBe(true) // 2. Check if cells in the row have background colors - const row = table.rows[rowIndex]; - expect(row).toBeTruthy(); + const row = table.rows[rowIndex] + expect(row).toBeTruthy() // Skip the first cell (text label) and check the rest const hasColoredCell = Array.from(row.cells).slice(1).some( - cell => (cell as HTMLElement).style.backgroundColor - ); - expect(hasColoredCell).toBe(true); + (cell) => (cell as HTMLElement).style.backgroundColor + ) + expect(hasColoredCell).toBe(true) // Clean up - removeHeatmap(table); - }); + removeHeatmap(table) + }) it('should skip non-numeric cells in row heatmap', () => { - const table = createTestTable(); + const table = createTestTable() // Add a non-numeric value to the row - table.rows[1].cells[1].textContent = 'N/A'; + table.rows[1].cells[1].textContent = 'N/A' // Apply heatmap to the row - toggleHeatmap(table, 1, 'row'); + toggleHeatmap(table, 1, 'row') // The non-numeric cell should not have heatmap styles - const nonNumericCell = table.rows[1].cells[1]; - expect(nonNumericCell.classList.contains('gs-heatmap-cell')).toBe(false); - expect(nonNumericCell.style.backgroundColor).toBeFalsy(); + const nonNumericCell = table.rows[1].cells[1] + expect(nonNumericCell.classList.contains('gs-heatmap-cell')).toBe(false) + expect(nonNumericCell.style.backgroundColor).toBeFalsy() // Clean up - removeHeatmap(table); - }); - }); + removeHeatmap(table) + }) + }) describe('Split Cell Cleanup', () => { it('should clean up split cell styles when removing heatmap', () => { - const table = createTestTable(); + const table = createTestTable() // Apply both row and column heatmaps to create a split cell - applyHeatmap(table, 1, 'column'); - applyHeatmap(table, 1, 'row'); + applyHeatmap(table, 1, 'column') + applyHeatmap(table, 1, 'row') // Get the cell at the intersection - const cell = getCell(table, 1, 1); + const cell = getCell(table, 1, 1) // Verify split cell styles are applied - expect(cell.classList.contains('gs-heatmap-split')).toBe(true); - expect(cell.style.getPropertyValue('--split-color-1')).toBeTruthy(); - expect(cell.style.getPropertyValue('--split-color-2')).toBeTruthy(); + expect(cell.classList.contains('gs-heatmap-split')).toBe(true) + expect(cell.style.getPropertyValue('--split-color-1')).toBeTruthy() + expect(cell.style.getPropertyValue('--split-color-2')).toBeTruthy() // Remove one of the heatmaps - removeHeatmap(table, 1, 'column'); + removeHeatmap(table, 1, 'column') // Verify split cell styles are cleaned up - expect(cell.classList.contains('gs-heatmap-split')).toBe(false); - expect(cell.style.getPropertyValue('--split-color-1')).toBe(''); - expect(cell.style.getPropertyValue('--split-color-2')).toBe(''); + expect(cell.classList.contains('gs-heatmap-split')).toBe(false) + expect(cell.style.getPropertyValue('--split-color-1')).toBe('') + expect(cell.style.getPropertyValue('--split-color-2')).toBe('') // The other heatmap should still be active - expect(cell.classList.contains('gs-heatmap-cell')).toBe(true); - expect(cell.style.backgroundColor).toBeTruthy(); + expect(cell.classList.contains('gs-heatmap-cell')).toBe(true) + expect(cell.style.backgroundColor).toBeTruthy() // Clean up - removeHeatmap(table); - }); - }); + removeHeatmap(table) + }) + }) describe('Edge Cases', () => { it('should handle empty tables', () => { - const table = document.createElement('table'); - document.body.appendChild(table); + const table = document.createElement('table') + document.body.appendChild(table) // Should not throw errors - expect(() => toggleHeatmap(table, 0, 'column')).not.toThrow(); - expect(() => toggleHeatmap(table, 0, 'row')).not.toThrow(); + expect(() => toggleHeatmap(table, 0, 'column')).not.toThrow() + expect(() => toggleHeatmap(table, 0, 'row')).not.toThrow() // Clean up - document.body.removeChild(table); - }); + document.body.removeChild(table) + }) it('should handle removing heatmap from a table without heatmap', () => { - const table = createTestTable(); + const table = createTestTable() // Should not throw errors - expect(() => removeHeatmap(table)).not.toThrow(); - }); - }); -}); + expect(() => removeHeatmap(table)).not.toThrow() + }) + }) +}) diff --git a/src/enrichments/frequency.ts b/src/enrichments/frequency.ts index 9ec6b7e..0b6f0ed 100644 --- a/src/enrichments/frequency.ts +++ b/src/enrichments/frequency.ts @@ -1,7 +1,7 @@ -import { isCategoricalColumn } from '../core/type-detection'; -import { analyzeFrequencies } from '../utils/frequency'; +import { isCategoricalColumn } from '../core/type-detection' +import { analyzeFrequencies } from '../utils/frequency' -const FREQUENCY_CLASS = 'gs-frequency'; +const FREQUENCY_CLASS = 'gs-frequency' // Store frequency analysis info for each table declare global { @@ -27,57 +27,57 @@ export function applyFrequencyAnalysis( type: 'row' | 'column' = 'column' ): Array<[string, number, number]> { // Get the cells to analyze - const cells: HTMLTableCellElement[] = []; + const cells: HTMLTableCellElement[] = [] if (type === 'column') { // For columns, get all cells in the specified column index - const rows = table.tBodies[0]?.rows || table.rows; + const rows = table.tBodies[0]?.rows || table.rows for (let i = 0; i < rows.length; i++) { - const cell = rows[i].cells[index]; - if (cell) cells.push(cell); + const cell = rows[i].cells[index] + if (cell) cells.push(cell) } } else { // For rows, get all cells in the specified row - const row = table.rows[index]; + const row = table.rows[index] if (row) { for (let i = 0; i < row.cells.length; i++) { - cells.push(row.cells[i]); + cells.push(row.cells[i]) } } } // Extract cell values - const values = cells.map(cell => cell.textContent || ''); + const values = cells.map((cell) => cell.textContent || '') // Check if the column is categorical if (!isCategoricalColumn(values)) { - throw new Error('Frequency analysis can only be applied to categorical data'); + throw new Error('Frequency analysis can only be applied to categorical data') } // Calculate frequencies - const frequencies = analyzeFrequencies(values); + const frequencies = analyzeFrequencies(values) // Store the frequency info for cleanup if (!table._frequencyInfos) { - table._frequencyInfos = []; + table._frequencyInfos = [] } // Mark the row/column as having frequency analysis const header = type === 'column' ? table.rows[0]?.cells[index] - : table.rows[index]?.cells[0]; + : table.rows[index]?.cells[0] if (header) { - header.classList.add(`${FREQUENCY_CLASS}-header`); + header.classList.add(`${FREQUENCY_CLASS}-header`) table._frequencyInfos.push({ index, type, element: header - }); + }) } - return frequencies; + return frequencies } /** @@ -92,32 +92,32 @@ export function removeFrequencyAnalysis( type?: 'row' | 'column' ): void { if (!table._frequencyInfos || table._frequencyInfos.length === 0) { - return; + return } if (index !== undefined && type) { // Remove specific frequency analysis const infoIndex = table._frequencyInfos.findIndex( - info => info.index === index && info.type === type - ); + (info) => info.index === index && info.type === type + ) if (infoIndex !== -1) { - const info = table._frequencyInfos[infoIndex]; - info.element.classList.remove(`${FREQUENCY_CLASS}-header`); - table._frequencyInfos.splice(infoIndex, 1); + const info = table._frequencyInfos[infoIndex] + info.element.classList.remove(`${FREQUENCY_CLASS}-header`) + table._frequencyInfos.splice(infoIndex, 1) // Remove the array if it's empty if (table._frequencyInfos.length === 0) { - delete table._frequencyInfos; + delete table._frequencyInfos } } } else { // Remove all frequency analyses - table._frequencyInfos.forEach(info => { - info.element.classList.remove(`${FREQUENCY_CLASS}-header`); - }); + table._frequencyInfos.forEach((info) => { + info.element.classList.remove(`${FREQUENCY_CLASS}-header`) + }) - delete table._frequencyInfos; + delete table._frequencyInfos } } @@ -135,14 +135,14 @@ export function toggleFrequencyAnalysis( ): Array<[string, number, number]> | undefined { // Check if frequency analysis is already active const isActive = table._frequencyInfos?.some( - info => info.index === index && info.type === type - ); + (info) => info.index === index && info.type === type + ) if (isActive) { - removeFrequencyAnalysis(table, index, type); - return undefined; + removeFrequencyAnalysis(table, index, type) + return undefined } else { - return applyFrequencyAnalysis(table, index, type); + return applyFrequencyAnalysis(table, index, type) } } @@ -159,6 +159,6 @@ export function isFrequencyAnalysisActive( type: 'row' | 'column' ): boolean { return !!table._frequencyInfos?.some( - info => info.index === index && info.type === type - ); + (info) => info.index === index && info.type === type + ) } diff --git a/src/enrichments/heatmap-utils.ts b/src/enrichments/heatmap-utils.ts index 46a5f80..a69fa3b 100644 --- a/src/enrichments/heatmap-utils.ts +++ b/src/enrichments/heatmap-utils.ts @@ -16,8 +16,8 @@ export function normalizeValue( min: number, max: number ): number { - if (min === max) return 0; - return Math.max(0, Math.min(1, (value - min) / (max - min))); + if (min === max) return 0 + return Math.max(0, Math.min(1, (value - min) / (max - min))) } /** @@ -40,16 +40,16 @@ export function getColorForValue( max: number, colorScale: string[] ): string { - if (colorScale.length === 0) return ''; - if (colorScale.length === 1) return colorScale[0]; + if (colorScale.length === 0) return '' + if (colorScale.length === 1) return colorScale[0] - const normalized = normalizeValue(value, min, max); + const normalized = normalizeValue(value, min, max) const index = Math.min( colorScale.length - 1, Math.floor(normalized * colorScale.length) - ); + ) - return colorScale[Math.max(0, index)]; + return colorScale[Math.max(0, index)] } /** @@ -71,24 +71,24 @@ export function calculateMinMax( existingMin?: number, existingMax?: number ): { min: number; max: number } { - let min = existingMin ?? Number.POSITIVE_INFINITY; - let max = existingMax ?? Number.NEGATIVE_INFINITY; - let hasNumericValues = false; + let min = existingMin ?? Number.POSITIVE_INFINITY + let max = existingMax ?? Number.NEGATIVE_INFINITY + let hasNumericValues = false for (const value of values) { - const num = typeof value === 'number' ? value : parseFloat(value as string); + const num = typeof value === 'number' ? value : parseFloat(value as string) if (!isNaN(num)) { - hasNumericValues = true; - min = Math.min(min, num); - max = Math.max(max, num); + hasNumericValues = true + min = Math.min(min, num) + max = Math.max(max, num) } } if (!hasNumericValues) { - return { min: 0, max: 0 }; + return { min: 0, max: 0 } } - return { min, max }; + return { min, max } } /** @@ -109,45 +109,45 @@ export function extractNumericValues( cells: HTMLTableCellElement[], options: { skipHeader?: boolean } = {} ): number[] { - const values: number[] = []; - const cellArray = Array.isArray(cells) ? cells : Array.from(cells); + const values: number[] = [] + const cellArray = Array.isArray(cells) ? cells : Array.from(cells) // If skipHeader is true, we need to determine how many cells are in the first row // to skip all of them if (options.skipHeader && cellArray.length > 0) { // Find the first row by traversing up to the row element - const firstCell = cellArray[0] as HTMLTableCellElement; - const firstRow = firstCell.closest('tr'); + const firstCell = cellArray[0] as HTMLTableCellElement + const firstRow = firstCell.closest('tr') if (firstRow) { // Skip all cells that are in the first row - const headerCells = Array.from(firstRow.cells); + const headerCells = Array.from(firstRow.cells) const filteredCells = cellArray.filter( (cell): cell is HTMLTableCellElement => !headerCells.includes(cell as HTMLTableCellElement) - ); + ) // Process remaining cells for (const cell of filteredCells) { - const text = cell.textContent?.trim() || ''; - const num = parseFloat(text); + const text = cell.textContent?.trim() || '' + const num = parseFloat(text) if (!isNaN(num)) { - values.push(num); + values.push(num) } } - return values; + return values } } // Default processing when not skipping header or no header found for (const cell of cellArray as HTMLTableCellElement[]) { - const text = cell.textContent?.trim() || ''; - const num = parseFloat(text); + const text = cell.textContent?.trim() || '' + const num = parseFloat(text) if (!isNaN(num)) { - values.push(num); + values.push(num) } } - return values; + return values } diff --git a/src/enrichments/heatmap.ts b/src/enrichments/heatmap.ts index 0574c0b..283f3f7 100644 --- a/src/enrichments/heatmap.ts +++ b/src/enrichments/heatmap.ts @@ -1,9 +1,9 @@ -import { cleanNumericCell } from '../core/type-detection'; +import { cleanNumericCell } from '../core/type-detection' // Define heatmap type to include 'table' for table-wide heatmaps export type HeatmapType = 'row' | 'column' | 'table'; -const HEATMAP_CLASS = 'gs-heatmap'; +const HEATMAP_CLASS = 'gs-heatmap' // Color scale for the heatmap (from light yellow to dark red) const HEATMAP_COLORS = [ @@ -13,8 +13,8 @@ const HEATMAP_COLORS = [ '#fdbb84', '#fc8d59', '#ef6548', - '#d7301f', // darkest -]; + '#d7301f' // darkest +] interface HeatmapOptions { minValue?: number; @@ -44,7 +44,7 @@ declare global { } // Add hover and split cell styles for heatmaps -const style = document.createElement('style'); +const style = document.createElement('style') style.textContent = ` .gs-heatmap-cell { transition: all 0.2s ease; @@ -73,32 +73,32 @@ style.textContent = ` background: linear-gradient(135deg, var(--split-color-1, transparent) 0%, var(--split-color-1, transparent) 50%, var(--split-color-2, transparent) 50%, var(--split-color-2, transparent) 100%); pointer-events: none; z-index: -1; - }`; + }` // Only add the style once if (!document.head.querySelector('style[data-heatmap-styles]')) { - style.setAttribute('data-heatmap-styles', ''); - document.head.appendChild(style); + style.setAttribute('data-heatmap-styles', '') + document.head.appendChild(style) } // Track active heatmaps by table and index type HeatmapKey = `${string}-${number}-${HeatmapType}`; -const activeHeatmaps = new Set(); +const activeHeatmaps = new Set() function getHeatmapKey(table: HTMLTableElement, index: number, type: HeatmapType): HeatmapKey { - return `${table.id || table.dataset.gsId || 'table'}-${index}-${type}`; + return `${table.id || table.dataset.gsId || 'table'}-${index}-${type}` } export function isHeatmapActive(table: HTMLTableElement, index: number, type: HeatmapType): boolean { - const key = getHeatmapKey(table, index, type); - return activeHeatmaps.has(key); + const key = getHeatmapKey(table, index, type) + return activeHeatmaps.has(key) } function setHeatmapActive(table: HTMLTableElement, index: number, type: HeatmapType, active: boolean): void { - const key = getHeatmapKey(table, index, type); + const key = getHeatmapKey(table, index, type) if (active) { - activeHeatmaps.add(key); + activeHeatmaps.add(key) } else { - activeHeatmaps.delete(key); + activeHeatmaps.delete(key) } } @@ -110,128 +110,128 @@ export function applyHeatmap( ): void { // Handle table type separately - delegate to applyTableHeatmap if (type === 'table') { - applyTableHeatmap(table, options); - return; + applyTableHeatmap(table, options) + return } if (isHeatmapActive(table, index, type)) { - return; + return } - const { minValue, maxValue, colorScale = HEATMAP_COLORS } = options; + const { minValue, maxValue, colorScale = HEATMAP_COLORS } = options // Get all cells in the specified row or column - const cells: HTMLTableCellElement[] = []; - const values: number[] = []; + const cells: HTMLTableCellElement[] = [] + const values: number[] = [] // At this point, type can only be 'row' or 'column' - const heatmapType = type as 'row' | 'column'; + const heatmapType = type as 'row' | 'column' if (heatmapType === 'column') { // For columns, get all cells in the specified column index (1-based for querySelector) // Try with tbody first, then fall back to direct tr children if no tbody exists - const hasTbody = !!table.querySelector('tbody'); + const hasTbody = !!table.querySelector('tbody') const selector = hasTbody ? `tbody tr:not(.gs-header-row) td:nth-child(${index + 1})` - : `tr:not(.gs-header-row) td:nth-child(${index + 1})`; + : `tr:not(.gs-header-row) td:nth-child(${index + 1})` - const columnCells = table.querySelectorAll(selector); + const columnCells = table.querySelectorAll(selector) columnCells.forEach((cell) => { - const value = cleanNumericCell(cell.textContent || ''); + const value = cleanNumericCell(cell.textContent || '') if (value !== null) { - cells.push(cell); - values.push(value); + cells.push(cell) + values.push(value) } - }); + }) } else { // For rows, get all cells in the specified row index (1-based for rows) - const row = table.querySelector(`tbody tr:nth-child(${index})`); + const row = table.querySelector(`tbody tr:nth-child(${index})`) if (row) { Array.from(row.cells).forEach((cell, cellIndex) => { // Skip the first cell if it's a row header if (cellIndex === 0 && cell.closest('th')) { - return; + return } - const value = cleanNumericCell(cell.textContent || ''); + const value = cleanNumericCell(cell.textContent || '') if (value !== null) { - cells.push(cell); - values.push(value); + cells.push(cell) + values.push(value) } - }); + }) } } if (values.length === 0) { - console.warn('No numeric values found for heatmap'); - return; + console.warn('No numeric values found for heatmap') + return } // Calculate min and max values if not provided - const min = minValue !== undefined ? minValue : Math.min(...values); - const max = maxValue !== undefined ? maxValue : Math.max(...values); - const range = max - min; + const min = minValue !== undefined ? minValue : Math.min(...values) + const max = maxValue !== undefined ? maxValue : Math.max(...values) + const range = max - min // Track cell elements for cleanup and their heatmap types - const trackedCellElements: Array = []; + const trackedCellElements: Array = [] // Function to apply or update cell styling const applyCellStyle = (cell: HTMLElement, color: string, heatmapType: 'row' | 'column') => { // Check if this cell already has a heatmap of a different type - const existingType = cell.dataset.heatmapType as 'row' | 'column' | undefined; + const existingType = cell.dataset.heatmapType as 'row' | 'column' | undefined if (existingType && existingType !== heatmapType) { // This cell has both row and column heatmaps - apply split style - cell.classList.add('gs-heatmap-split'); - cell.style.setProperty('--split-color-1', existingType === 'row' ? cell.style.backgroundColor || '' : color); - cell.style.setProperty('--split-color-2', existingType === 'column' ? cell.style.backgroundColor || '' : color); - cell.style.backgroundColor = ''; // Clear solid background + cell.classList.add('gs-heatmap-split') + cell.style.setProperty('--split-color-1', existingType === 'row' ? cell.style.backgroundColor || '' : color) + cell.style.setProperty('--split-color-2', existingType === 'column' ? cell.style.backgroundColor || '' : color) + cell.style.backgroundColor = '' // Clear solid background } else { // Single heatmap type for this cell - cell.style.backgroundColor = color; - cell.classList.remove('gs-heatmap-split'); - cell.style.removeProperty('--split-color-1'); - cell.style.removeProperty('--split-color-2'); + cell.style.backgroundColor = color + cell.classList.remove('gs-heatmap-split') + cell.style.removeProperty('--split-color-1') + cell.style.removeProperty('--split-color-2') } // Track the heatmap type for this cell - cell.dataset.heatmapType = heatmapType; - }; + cell.dataset.heatmapType = heatmapType + } if (range === 0) { // All values are the same, use the middle color - const colorIndex = Math.floor(colorScale.length / 2); - const color = colorScale[colorIndex]; + const colorIndex = Math.floor(colorScale.length / 2) + const color = colorScale[colorIndex] // Apply color to all cells - cells.forEach(cell => { - applyCellStyle(cell, color, type); - trackedCellElements.push({ element: cell, heatmapType: type, style: cell.style }); - }); + cells.forEach((cell) => { + applyCellStyle(cell, color, type) + trackedCellElements.push({ element: cell, heatmapType: type, style: cell.style }) + }) } else { // Different values, calculate color for each cell cells.forEach((cell, idx) => { - const normalized = (values[idx] - min) / range; + const normalized = (values[idx] - min) / range const colorIndex = Math.min( colorScale.length - 1, Math.max(0, Math.floor(normalized * colorScale.length)) - ); + ) - applyCellStyle(cell, colorScale[colorIndex], type); - trackedCellElements.push({ element: cell, heatmapType: type, style: cell.style }); - }); + applyCellStyle(cell, colorScale[colorIndex], type) + trackedCellElements.push({ element: cell, heatmapType: type, style: cell.style }) + }) } // Initialize _heatmapInfos if it doesn't exist if (!table._heatmapInfos) { - table._heatmapInfos = []; + table._heatmapInfos = [] } // Track the active heatmap - setHeatmapActive(table, index, type, true); + setHeatmapActive(table, index, type, true) // Add the heatmap class to the table - table.classList.add(HEATMAP_CLASS); + table.classList.add(HEATMAP_CLASS) // Store the heatmap info for cleanup table._heatmapInfos.push({ @@ -242,21 +242,21 @@ export function applyHeatmap( heatmapType, style: element.style })) - }); + }) // Force a reflow to ensure styles are applied before the test checks them // This is needed for the test environment if (process.env.NODE_ENV === 'test') { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - table.offsetHeight; + + table.offsetHeight } // Dispatch event to notify about the heatmap change const event = new CustomEvent('gridsight:heatmapChanged', { bubbles: true, detail: { table, index, type, active: true } - }); - table.dispatchEvent(event); + }) + table.dispatchEvent(event) } // Helper function to clean up a cell's heatmap styles and classes @@ -264,104 +264,104 @@ function cleanupCell(cell: HTMLElement, heatmapType: HeatmapType): void { // If this cell has a split, we need to handle it specially if (cell.classList.contains('gs-heatmap-split')) { // Get the other heatmap type - const otherType = heatmapType === 'row' ? 'column' : 'row'; - const otherColor = cell.style.getPropertyValue(`--split-color-${otherType === 'row' ? '1' : '2'}`); + const otherType = heatmapType === 'row' ? 'column' : 'row' + const otherColor = cell.style.getPropertyValue(`--split-color-${otherType === 'row' ? '1' : '2'}`) if (otherColor) { // Restore the other heatmap's color - cell.style.backgroundColor = otherColor; + cell.style.backgroundColor = otherColor } else { // No other heatmap, clear the background - cell.style.removeProperty('background-color'); + cell.style.removeProperty('background-color') } // Clean up split-related styles - cell.classList.remove('gs-heatmap-split'); - cell.style.removeProperty('--split-color-1'); - cell.style.removeProperty('--split-color-2'); + cell.classList.remove('gs-heatmap-split') + cell.style.removeProperty('--split-color-1') + cell.style.removeProperty('--split-color-2') } else { // Regular cell, just remove the background color - cell.style.removeProperty('background-color'); + cell.style.removeProperty('background-color') } // Remove the heatmap class and data attribute - cell.classList.remove('gs-heatmap-cell'); - delete cell.dataset.heatmapType; + cell.classList.remove('gs-heatmap-cell') + delete cell.dataset.heatmapType } export function removeHeatmap(table: HTMLTableElement, index?: number, type?: HeatmapType): void { if (!table._heatmapInfos || table._heatmapInfos.length === 0) { - return; + return } if (index !== undefined && type) { // Special case for table-wide heatmap - remove all heatmaps if (type === 'table') { // Remove all heatmaps as table-wide heatmap affects all cells - removeHeatmap(table); - return; + removeHeatmap(table) + return } // Remove specific heatmap const heatmapIndex = table._heatmapInfos.findIndex( - h => h.index === index && h.type === type - ); + (h) => h.index === index && h.type === type + ) if (heatmapIndex !== -1) { - const heatmap = table._heatmapInfos[heatmapIndex]; + const heatmap = table._heatmapInfos[heatmapIndex] // Remove cell styles heatmap.cellElements.forEach(({ element, heatmapType }) => { - cleanupCell(element, heatmapType); - }); + cleanupCell(element, heatmapType) + }) // Remove from active heatmaps - setHeatmapActive(table, index, type, false); + setHeatmapActive(table, index, type, false) // Remove from the table's heatmap infos - table._heatmapInfos.splice(heatmapIndex, 1); + table._heatmapInfos.splice(heatmapIndex, 1) // If no more heatmaps, remove the heatmap class from the table if (table._heatmapInfos.length === 0) { - table.classList.remove(HEATMAP_CLASS); + table.classList.remove(HEATMAP_CLASS) } // Dispatch event for this specific heatmap removal const event = new CustomEvent('gridsight:heatmapChanged', { bubbles: true, detail: { table, index, type, active: false } - }); - table.dispatchEvent(event); + }) + table.dispatchEvent(event) - return; + return } } else { // Remove all heatmaps - const heatmapsToRemove = [...(table._heatmapInfos || [])]; + const heatmapsToRemove = [...(table._heatmapInfos || [])] - heatmapsToRemove.forEach(heatmap => { + heatmapsToRemove.forEach((heatmap) => { // Remove cell styles heatmap.cellElements.forEach(({ element, heatmapType }) => { - cleanupCell(element, heatmapType); - }); + cleanupCell(element, heatmapType) + }) // Remove from active heatmaps - setHeatmapActive(table, heatmap.index, heatmap.type, false); - }); + setHeatmapActive(table, heatmap.index, heatmap.type, false) + }) // Clear all heatmap infos - table._heatmapInfos = []; + table._heatmapInfos = [] // Remove the heatmap class - table.classList.remove(HEATMAP_CLASS); + table.classList.remove(HEATMAP_CLASS) // Dispatch event for all heatmaps removed const event = new CustomEvent('gridsight:heatmapChanged', { bubbles: true, detail: { table, active: false } - }); - table.dispatchEvent(event); + }) + table.dispatchEvent(event) } } @@ -372,123 +372,123 @@ export function removeHeatmap(table: HTMLTableElement, index?: number, type?: He */ export function applyTableHeatmap(table: HTMLTableElement, options: HeatmapOptions = {}): void { if (isHeatmapActive(table, -1, 'table')) { - return; + return } // Get all data cells in the table - const cells: HTMLTableCellElement[] = []; - const values: number[] = []; + const cells: HTMLTableCellElement[] = [] + const values: number[] = [] // Get all rows in the table - const rows = Array.from(table.rows); + const rows = Array.from(table.rows) // Skip the first row (headers) and process the rest for (let rowIndex = 1; rowIndex < rows.length; rowIndex++) { - const row = rows[rowIndex]; + const row = rows[rowIndex] // Get all cells in the row - const rowCells = Array.from(row.cells); + const rowCells = Array.from(row.cells) // Process each cell - rowCells.forEach(cell => { + rowCells.forEach((cell) => { // Skip header cells (th elements or cells with header role) if (cell.tagName.toLowerCase() === 'th' || cell.getAttribute('role') === 'rowheader') { - return; + return } // Check if the cell contains a numeric value - const cellText = cell.textContent?.trim() || ''; - const numericValue = cleanNumericCell(cellText); + const cellText = cell.textContent?.trim() || '' + const numericValue = cleanNumericCell(cellText) if (numericValue !== null) { - cells.push(cell); - values.push(numericValue); + cells.push(cell) + values.push(numericValue) } - }); + }) } if (values.length === 0) { - console.warn('No numeric values found for table-wide heatmap'); - return; + console.warn('No numeric values found for table-wide heatmap') + return } // Calculate min and max values if not provided - const { minValue, maxValue, colorScale = HEATMAP_COLORS } = options; - const min = minValue !== undefined ? minValue : Math.min(...values); - const max = maxValue !== undefined ? maxValue : Math.max(...values); - const range = max - min; + const { minValue, maxValue, colorScale = HEATMAP_COLORS } = options + const min = minValue !== undefined ? minValue : Math.min(...values) + const max = maxValue !== undefined ? maxValue : Math.max(...values) + const range = max - min // Track cell elements for cleanup - const trackedCellElements: Array = []; + const trackedCellElements: Array = [] // Use a special index for table-wide heatmap - const tableHeatmapIndex = -1; // Use -1 as a special index for table-wide heatmap + const tableHeatmapIndex = -1 // Use -1 as a special index for table-wide heatmap // Check if table-wide heatmap is already active if (isHeatmapActive(table, tableHeatmapIndex, 'table')) { // If active, remove it - removeHeatmap(table, tableHeatmapIndex, 'table'); - return; + removeHeatmap(table, tableHeatmapIndex, 'table') + return } if (range === 0) { // All values are the same, use the middle color - const colorIndex = Math.floor(colorScale.length / 2); - const color = colorScale[colorIndex]; + const colorIndex = Math.floor(colorScale.length / 2) + const color = colorScale[colorIndex] // Apply color to all cells - cells.forEach(cell => { - cell.style.backgroundColor = color; - cell.classList.add('gs-heatmap-cell'); - cell.dataset.heatmapType = 'table'; - trackedCellElements.push({ element: cell, heatmapType: 'table', style: cell.style }); - }); + cells.forEach((cell) => { + cell.style.backgroundColor = color + cell.classList.add('gs-heatmap-cell') + cell.dataset.heatmapType = 'table' + trackedCellElements.push({ element: cell, heatmapType: 'table', style: cell.style }) + }) } else { // Different values, calculate color for each cell cells.forEach((cell, idx) => { - const normalized = (values[idx] - min) / range; + const normalized = (values[idx] - min) / range const colorIndex = Math.min( colorScale.length - 1, Math.max(0, Math.floor(normalized * colorScale.length)) - ); + ) - cell.style.backgroundColor = colorScale[colorIndex]; - cell.classList.add('gs-heatmap-cell'); - cell.dataset.heatmapType = 'table'; - trackedCellElements.push({ element: cell, heatmapType: 'table', style: cell.style }); - }); + cell.style.backgroundColor = colorScale[colorIndex] + cell.classList.add('gs-heatmap-cell') + cell.dataset.heatmapType = 'table' + trackedCellElements.push({ element: cell, heatmapType: 'table', style: cell.style }) + }) } // Initialize _heatmapInfos if it doesn't exist if (!table._heatmapInfos) { - table._heatmapInfos = []; + table._heatmapInfos = [] } // Track the active heatmap - setHeatmapActive(table, tableHeatmapIndex, 'table', true); + setHeatmapActive(table, tableHeatmapIndex, 'table', true) // Add the heatmap class to the table - table.classList.add(HEATMAP_CLASS); + table.classList.add(HEATMAP_CLASS) // Store the heatmap info for cleanup table._heatmapInfos.push({ index: tableHeatmapIndex, type: 'table', cellElements: trackedCellElements - }); + }) // Force a reflow to ensure styles are applied before the test checks them // This is needed for the test environment if (process.env.NODE_ENV === 'test') { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - table.offsetHeight; + + table.offsetHeight } // Dispatch event to notify about the heatmap change const event = new CustomEvent('gridsight:heatmapChanged', { bubbles: true, detail: { table, index: tableHeatmapIndex, type: 'table', active: true } - }); - table.dispatchEvent(event); + }) + table.dispatchEvent(event) } export function toggleHeatmap( @@ -499,18 +499,18 @@ export function toggleHeatmap( ): void { if (type === 'table') { // For table-wide heatmap, delegate to applyTableHeatmap - applyTableHeatmap(table, options); - return; + applyTableHeatmap(table, options) + return } // Check if this specific heatmap is already active - const isActive = isHeatmapActive(table, index, type); + const isActive = isHeatmapActive(table, index, type) if (isActive) { // If it's active, remove just this specific heatmap - removeHeatmap(table, index, type); + removeHeatmap(table, index, type) } else { // If not active, apply the new heatmap (can have multiple heatmaps on different rows/columns) - applyHeatmap(table, index, type, options); + applyHeatmap(table, index, type, options) } } diff --git a/src/enrichments/statistics.ts b/src/enrichments/statistics.ts index 0d9f88f..32f6d5b 100644 --- a/src/enrichments/statistics.ts +++ b/src/enrichments/statistics.ts @@ -6,7 +6,7 @@ import { standardDeviation, variance as sampleVariance, sum -} from 'simple-statistics'; +} from 'simple-statistics' export interface StatisticsResult { count: number; @@ -26,14 +26,14 @@ export interface StatisticsResult { */ export function calculateStatistics(values: number[]): StatisticsResult { if (!values.length) { - throw new Error('Cannot calculate statistics for an empty array'); + throw new Error('Cannot calculate statistics for an empty array') } // Filter out any non-finite numbers - const validValues = values.filter(Number.isFinite); + const validValues = values.filter(Number.isFinite) if (validValues.length === 0) { - throw new Error('No valid numeric values provided'); + throw new Error('No valid numeric values provided') } return { @@ -45,7 +45,7 @@ export function calculateStatistics(values: number[]): StatisticsResult { median: median(validValues), stdDev: standardDeviation(validValues), variance: sampleVariance(validValues) - }; + } } /** @@ -55,11 +55,11 @@ export function calculateStatistics(values: number[]): StatisticsResult { * @returns Formatted number as a string */ export function formatNumber(value: number, decimals: number = 2): string { - if (!Number.isFinite(value)) return 'N/A'; + if (!Number.isFinite(value)) return 'N/A' return value.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals - }); + }) } /** @@ -77,5 +77,5 @@ export function formatStatistics(stats: StatisticsResult): string { `Median: ${formatNumber(stats.median)}`, `Std Dev: ${formatNumber(stats.stdDev)}`, `Variance: ${formatNumber(stats.variance)}` - ].join('\n'); + ].join('\n') } diff --git a/src/index.ts b/src/index.ts index 0b979bd..e442244 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,9 +6,9 @@ */ // Import core modules -import { processTable } from './core/table-processor'; -import type { TableProcessorOptions } from './core/table-processor'; -import { detectColumnTypes, extractTableData } from './core/type-detection'; +import { processTable } from './core/table-processor' +import type { TableProcessorOptions } from './core/table-processor' +import { detectColumnTypes, extractTableData } from './core/type-detection' // Define HeatmapOptions interface since it's not exported from heatmap.ts interface HeatmapOptions { @@ -23,22 +23,22 @@ import { removeHeatmap, toggleHeatmap, isHeatmapActive -} from './enrichments/heatmap'; +} from './enrichments/heatmap' // Import UI components -import { injectToggle } from './ui/toggle-injector'; +import { injectToggle } from './ui/toggle-injector' // Re-export types for external use export type { TableProcessorOptions, HeatmapOptions -}; +} /** * The main GridSight API object that will be exposed to the window */ // Table state management -const tableRegistry = new Map(); +const tableRegistry = new Map() /** * The main GridSight API object that will be exposed to the window @@ -57,19 +57,19 @@ const GridSight = { document.querySelectorAll('table').forEach((table, index) => { // Skip tables that don't meet our validity criteria if (!this.isValidTable(table)) { - console.warn(`Skipping invalid table at index ${index}: Table must have at least two rows`); - return; + console.warn(`Skipping invalid table at index ${index}: Table must have at least two rows`) + return } try { this.processTable(table, { id: `table-${index}`, ...options - }); + }) } catch (error) { - console.error(`Failed to process table ${index}:`, error); + console.error(`Failed to process table ${index}:`, error) } - }); - return this; + }) + return this }, /** @@ -79,28 +79,28 @@ const GridSight = { */ processTable(table: HTMLTableElement, options: TableProcessorOptions = {}) { if (!table) { - throw new Error('No table element provided'); + throw new Error('No table element provided') } // Ensure the table has an ID if (!table.id) { - table.id = options.id || `grid-sight-${Math.random().toString(36).substr(2, 9)}`; + table.id = options.id || `grid-sight-${Math.random().toString(36).substr(2, 9)}` } // Process the table - const processedTable = processTable(table, options); + const processedTable = processTable(table, options) // Add to registry - tableRegistry.set(table.id, table); + tableRegistry.set(table.id, table) try { // Inject toggle which will handle the enrichment menu - injectToggle(table); + injectToggle(table) } catch (error) { - console.warn('Failed to inject UI elements:', error); + console.warn('Failed to inject UI elements:', error) } - return processedTable; + return processedTable }, /** @@ -111,12 +111,12 @@ const GridSight = { isValidTable(table: HTMLTableElement | null): boolean { // Check if table exists and is an HTMLTableElement if (!table || !(table instanceof HTMLTableElement)) { - return false; + return false } // Check if table has at least two rows (one for header, one for data) - const rowCount = table.rows.length; - return rowCount >= 2; + const rowCount = table.rows.length + return rowCount >= 2 }, /** @@ -124,14 +124,14 @@ const GridSight = { * @param id The ID of the table to retrieve */ getTableById(id: string): HTMLTableElement | undefined { - return tableRegistry.get(id); + return tableRegistry.get(id) }, /** * Get all processed tables */ getAllTables(): HTMLTableElement[] { - return Array.from(tableRegistry.values()); + return Array.from(tableRegistry.values()) }, /** @@ -147,11 +147,11 @@ const GridSight = { type: 'row' | 'column' = 'column', options: HeatmapOptions = {} ): void { - const targetTable = typeof table === 'string' ? this.getTableById(table) : table; + const targetTable = typeof table === 'string' ? this.getTableById(table) : table if (!targetTable) { - throw new Error('Table not found'); + throw new Error('Table not found') } - applyHeatmap(targetTable, index, type, options); + applyHeatmap(targetTable, index, type, options) }, /** @@ -166,12 +166,12 @@ const GridSight = { type?: 'row' | 'column' ): void { if (typeof table === 'string') { - const targetTable = this.getTableById(table); + const targetTable = this.getTableById(table) if (targetTable) { - removeHeatmap(targetTable, index, type); + removeHeatmap(targetTable, index, type) } } else { - removeHeatmap(table, index, type); + removeHeatmap(table, index, type) } }, @@ -188,11 +188,11 @@ const GridSight = { type: 'row' | 'column' = 'column', options: HeatmapOptions = {} ): void { - const targetTable = typeof table === 'string' ? this.getTableById(table) : table; + const targetTable = typeof table === 'string' ? this.getTableById(table) : table if (!targetTable) { - throw new Error('Table not found'); + throw new Error('Table not found') } - toggleHeatmap(targetTable, index, type, options); + toggleHeatmap(targetTable, index, type, options) }, /** @@ -206,11 +206,11 @@ const GridSight = { index: number, type: 'row' | 'column' = 'column' ): boolean { - const targetTable = typeof table === 'string' ? this.getTableById(table) : table; + const targetTable = typeof table === 'string' ? this.getTableById(table) : table if (!targetTable) { - return false; + return false } - return isHeatmapActive(targetTable, index, type); + return isHeatmapActive(targetTable, index, type) }, /** @@ -222,17 +222,17 @@ const GridSight = { table: HTMLTableElement | string, columnIndex: number ): string { - const targetTable = typeof table === 'string' ? this.getTableById(table) : table; + const targetTable = typeof table === 'string' ? this.getTableById(table) : table if (!targetTable) { - throw new Error('Table not found'); + throw new Error('Table not found') } // Extract table data first - const tableData = extractTableData(targetTable); + const tableData = extractTableData(targetTable) // Then detect column types - const types = detectColumnTypes(tableData); - return types[columnIndex] || 'unknown'; + const types = detectColumnTypes(tableData) + return types[columnIndex] || 'unknown' }, /** @@ -242,18 +242,18 @@ const GridSight = { getTableStructure( table: HTMLTableElement | string ): { rows: number; cols: number; hasHeader: boolean } { - const targetTable = typeof table === 'string' ? this.getTableById(table) : table; + const targetTable = typeof table === 'string' ? this.getTableById(table) : table if (!targetTable) { - throw new Error('Table not found'); + throw new Error('Table not found') } // Extract table data and analyze its structure - const tableData = extractTableData(targetTable); + const tableData = extractTableData(targetTable) return { rows: tableData.length, cols: tableData[0]?.length || 0, hasHeader: this.detectIfTableHasHeader(targetTable) - }; + } }, /** @@ -264,54 +264,54 @@ const GridSight = { detectIfTableHasHeader(table: HTMLTableElement): boolean { // Simple heuristic: check if the first row contains mostly text content // and the second row contains more varied content - if (table.rows.length < 2) return false; + if (table.rows.length < 2) return false - const firstRow = table.rows[0]; - const secondRow = table.rows[1]; + const firstRow = table.rows[0] + const secondRow = table.rows[1] // Count non-empty cells in first row const firstRowNonEmpty = Array.from(firstRow.cells).filter( - cell => cell.textContent && cell.textContent.trim() !== '' - ).length; + (cell) => cell.textContent && cell.textContent.trim() !== '' + ).length // If first row is empty, it's probably not a header - if (firstRowNonEmpty === 0) return false; + if (firstRowNonEmpty === 0) return false // If first row has significantly fewer non-empty cells than second row, // it's probably not a header const secondRowNonEmpty = Array.from(secondRow.cells).filter( - cell => cell.textContent && cell.textContent.trim() !== '' - ).length; + (cell) => cell.textContent && cell.textContent.trim() !== '' + ).length - return firstRowNonEmpty >= secondRowNonEmpty; + return firstRowNonEmpty >= secondRowNonEmpty } -}; +} // Auto-initialize when the DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { - GridSight.init(); - }); + GridSight.init() + }) } else { // DOMContentLoaded has already fired, run immediately - setTimeout(() => GridSight.init(), 0); + setTimeout(() => GridSight.init(), 0) } // Export the GridSight API -export default GridSight; +export default GridSight // Expose to window for direct script include // Use a more direct approach to ensure it's available globally if (typeof window !== 'undefined') { - (window as any).gridSight = GridSight; + (window as any).gridSight = GridSight } // Also assign to globalThis for better compatibility if (typeof globalThis !== 'undefined') { - (globalThis as any).gridSight = GridSight; + (globalThis as any).gridSight = GridSight } // For CommonJS environments if (typeof module !== 'undefined' && module.exports) { - module.exports = GridSight; + module.exports = GridSight } diff --git a/src/main.ts b/src/main.ts index 0be0a45..5a433c0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,9 +1,9 @@ -import './style.css'; -import { findSuitableTables } from './core/table-detection'; -import { injectToggle } from './ui/toggle-injector'; +import './style.css' +import { findSuitableTables } from './core/table-detection' +import { injectToggle } from './ui/toggle-injector' // Debug flag - set to false in production -const DEBUG = false; +const DEBUG = false /** * Logs debug information if debug mode is enabled. @@ -12,7 +12,7 @@ const DEBUG = false; */ function debugLog(message: string, data?: unknown): void { if (DEBUG) { - console.log(`[GridSight] ${message}`, data || ''); + console.log(`[GridSight] ${message}`, data || '') } } @@ -22,34 +22,34 @@ function debugLog(message: string, data?: unknown): void { * Grid-Sight toggle into the ones that are suitable. */ function initializeGridSight(): void { - debugLog('Initializing Grid-Sight...'); + debugLog('Initializing Grid-Sight...') const { suitableTables, totalTables } = findSuitableTables().reduce( (result, { table, isSuitable, reason, columnTypes }) => { debugLog(`Table analysis: ${isSuitable ? 'Suitable' : 'Not suitable'} - ${reason}`, { columnTypes, table: table.id ? `#${table.id}` : 'anonymous table' - }); + }) if (isSuitable) { - injectToggle(table); - result.suitableTables++; + injectToggle(table) + result.suitableTables++ } - result.totalTables++; - return result; + result.totalTables++ + return result }, { suitableTables: 0, totalTables: 0 } - ); + ) - debugLog(`Initialization complete. Found ${suitableTables} suitable tables out of ${totalTables} total tables.`); + debugLog(`Initialization complete. Found ${suitableTables} suitable tables out of ${totalTables} total tables.`) } // Run the initialization logic once the DOM is fully loaded. -window.addEventListener('DOMContentLoaded', initializeGridSight); +window.addEventListener('DOMContentLoaded', initializeGridSight) // Export for potential use in other modules or for testing. export { initializeGridSight, debugLog // Export for testing -}; +} diff --git a/src/stories/Button.stories.ts b/src/stories/Button.stories.ts index 1597f3c..76bb312 100644 --- a/src/stories/Button.stories.ts +++ b/src/stories/Button.stories.ts @@ -1,9 +1,9 @@ -import type { Meta, StoryObj } from '@storybook/html-vite'; +import type { Meta, StoryObj } from '@storybook/html-vite' -import { fn } from 'storybook/test'; +import { fn } from 'storybook/test' -import type { ButtonProps } from './Button'; -import { createButton } from './Button'; +import type { ButtonProps } from './Button' +import { createButton } from './Button' // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export const meta = { @@ -12,7 +12,7 @@ const meta = { render: (args) => { // You can either use a function to create DOM elements or use a plain html string! // return `
${label}
`; - return createButton(args); + return createButton(args) }, argTypes: { backgroundColor: { control: 'color' }, @@ -21,40 +21,40 @@ const meta = { primary: { control: 'boolean' }, size: { control: { type: 'select' }, - options: ['small', 'medium', 'large'], - }, + options: ['small', 'medium', 'large'] + } }, // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args - args: { onClick: fn() }, -} satisfies Meta; + args: { onClick: fn() } +} satisfies Meta -export default meta; +export default meta type Story = StoryObj; // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args export const Primary: Story = { args: { primary: true, - label: 'Button', - }, -}; + label: 'Button' + } +} export const Secondary: Story = { args: { - label: 'Button', - }, -}; + label: 'Button' + } +} export const Large: Story = { args: { size: 'large', - label: 'Button', - }, -}; + label: 'Button' + } +} export const Small: Story = { args: { size: 'small', - label: 'Button', - }, -}; + label: 'Button' + } +} diff --git a/src/stories/Button.ts b/src/stories/Button.ts index 904a62a..b352b85 100644 --- a/src/stories/Button.ts +++ b/src/stories/Button.ts @@ -1,4 +1,4 @@ -import './button.css'; +import './button.css' export interface ButtonProps { /** Is this the principal call to action on the page? */ @@ -19,21 +19,21 @@ export const createButton = ({ size = 'medium', backgroundColor, label, - onClick, + onClick }: ButtonProps) => { - const btn = document.createElement('button'); - btn.type = 'button'; - btn.innerText = label; + const btn = document.createElement('button') + btn.type = 'button' + btn.innerText = label if (onClick) { - btn.addEventListener('click', onClick); + btn.addEventListener('click', onClick) } - const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; - btn.className = ['storybook-button', `storybook-button--${size}`, mode].join(' '); + const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary' + btn.className = ['storybook-button', `storybook-button--${size}`, mode].join(' ') if (backgroundColor) { - btn.style.backgroundColor = backgroundColor; + btn.style.backgroundColor = backgroundColor } - return btn; -}; + return btn +} diff --git a/src/stories/EnrichmentMenu.stories.ts b/src/stories/EnrichmentMenu.stories.ts index 5d1b5f3..38057a9 100644 --- a/src/stories/EnrichmentMenu.stories.ts +++ b/src/stories/EnrichmentMenu.stories.ts @@ -1,102 +1,102 @@ -import type { Meta, StoryObj } from '@storybook/html'; -import { expect, userEvent, within } from '@storybook/test'; -import { initializeGridSight } from '../main'; +import type { Meta, StoryObj } from '@storybook/html' +import { expect, userEvent, within } from '@storybook/test' +import { initializeGridSight } from '../main' // Import HTML table from external file -import testTable from './tables/numeric-and-categorical.html?raw'; +import testTable from './tables/numeric-and-categorical.html?raw' const meta: Meta = { title: 'Enrichment Menu', render: () => { // Create container for the table - const container = document.createElement('div'); - container.style.padding = '20px'; + const container = document.createElement('div') + container.style.padding = '20px' container.innerHTML = `

Test Table with Numeric and Categorical Columns

${testTable}
- `; + ` // Initialize GridSight after a short delay to ensure the table is in the DOM requestAnimationFrame(() => { - initializeGridSight(); - }); + initializeGridSight() + }) - return container; + return container }, parameters: { // Disable Storybook's default padding layout: 'fullscreen', // Add a background color to make the table stand out backgrounds: { default: '#f5f5f5' } - }, -}; + } +} -export default meta; +export default meta type Story = StoryObj; // Helper function to test if heatmap option is available for a column async function testHeatmapOptionForColumn(canvasElement: HTMLElement, columnName: string, shouldBeAvailable: boolean) { // Wait for GridSight to initialize - await new Promise(resolve => setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)) - const canvas = within(canvasElement); + const canvas = within(canvasElement) // First, find and click the GS toggle to enable GridSight - const gsToggle = canvasElement.querySelector('.grid-sight-toggle'); + const gsToggle = canvasElement.querySelector('.grid-sight-toggle') if (!gsToggle) { - throw new Error('GridSight toggle not found'); + throw new Error('GridSight toggle not found') } - await userEvent.click(gsToggle); + await userEvent.click(gsToggle) // Wait for GridSight to initialize - await new Promise(resolve => setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)) // Find the header for the specified column - const header = canvas.getByText(columnName).closest('th'); + const header = canvas.getByText(columnName).closest('th') if (!header) { - throw new Error(`${columnName} header not found`); + throw new Error(`${columnName} header not found`) } // Find the plus icon in the header - const plusIcon = header.querySelector('.gs-plus-icon'); + const plusIcon = header.querySelector('.gs-plus-icon') if (!plusIcon) { - throw new Error(`Plus icon not found in ${columnName} header. Make sure GridSight is enabled.`); + throw new Error(`Plus icon not found in ${columnName} header. Make sure GridSight is enabled.`) } // Click the plus icon to open the menu - await userEvent.click(plusIcon); + await userEvent.click(plusIcon) try { // Check if the enrichment menu is shown - const menu = document.querySelector('.gs-enrichment-menu'); - expect(menu).toBeInTheDocument(); + const menu = document.querySelector('.gs-enrichment-menu') + expect(menu).toBeInTheDocument() // Check if heatmap option is in the menu as expected - const heatmapOption = within(menu as HTMLElement).queryByText('Heatmap'); + const heatmapOption = within(menu as HTMLElement).queryByText('Heatmap') if (shouldBeAvailable) { - expect(heatmapOption).toBeInTheDocument(); + expect(heatmapOption).toBeInTheDocument() } else { - expect(heatmapOption).not.toBeInTheDocument(); + expect(heatmapOption).not.toBeInTheDocument() } } finally { // Always close the menu - await userEvent.click(document.body); + await userEvent.click(document.body) } } export const ShowsHeatmapForNumericColumns: Story = { name: 'Shows heatmap for numeric columns', play: async ({ canvasElement }) => { - await testHeatmapOptionForColumn(canvasElement, 'Price', true); - }, -}; + await testHeatmapOptionForColumn(canvasElement, 'Price', true) + } +} export const NoHeatmapForCategoricalColumns: Story = { name: 'No heatmap for categorical columns', play: async ({ canvasElement }) => { - await testHeatmapOptionForColumn(canvasElement, 'Category', false); - }, -}; + await testHeatmapOptionForColumn(canvasElement, 'Category', false) + } +} diff --git a/src/stories/TableToggle.stories.ts b/src/stories/TableToggle.stories.ts index d6f365b..9d5108c 100644 --- a/src/stories/TableToggle.stories.ts +++ b/src/stories/TableToggle.stories.ts @@ -1,13 +1,13 @@ -import type { Meta, StoryObj } from '@storybook/html'; -import { within, expect } from '@storybook/test'; -import { initializeGridSight } from '../main'; +import type { Meta, StoryObj } from '@storybook/html' +import { within, expect } from '@storybook/test' +import { initializeGridSight } from '../main' // Import table HTML content -import numericTable from './tables/numeric-and-categorical.html?raw'; -import categoricalTable from './tables/categorical.html?raw'; -import mixedTable from './tables/mixed.html?raw'; -import twoDimTable from './tables/two-dim-data.html?raw'; -import unsuitableTable from './tables/unsuitable.html?raw'; +import numericTable from './tables/numeric-and-categorical.html?raw' +import categoricalTable from './tables/categorical.html?raw' +import mixedTable from './tables/mixed.html?raw' +import twoDimTable from './tables/two-dim-data.html?raw' +import unsuitableTable from './tables/unsuitable.html?raw' // Define the metadata for the story const meta: Meta = { @@ -23,11 +23,11 @@ const meta: Meta = { } }, render: () => { - const container = document.createElement('div'); - container.style.maxWidth = '800px'; - container.style.margin = '0 auto'; - container.style.padding = '20px'; - container.style.fontFamily = 'Arial, sans-serif'; + const container = document.createElement('div') + container.style.maxWidth = '800px' + container.style.margin = '0 auto' + container.style.padding = '20px' + container.style.fontFamily = 'Arial, sans-serif' container.innerHTML = `