From 5444dffbd4a83b36ec15a31b676ce1c53113fd4d Mon Sep 17 00:00:00 2001 From: agatemosu Date: Wed, 4 Dec 2024 19:21:31 +0100 Subject: [PATCH 001/153] Upgrade packages --- package.json | 17 +- yarn.lock | 1700 +++++++++++++++++++++++++++++++++----------------- 2 files changed, 1148 insertions(+), 569 deletions(-) diff --git a/package.json b/package.json index 982bd78cd3c..2753aca3d93 100644 --- a/package.json +++ b/package.json @@ -102,15 +102,16 @@ "ziggy-js": "^1.8.1" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "^6.6.0", - "@typescript-eslint/parser": "^6.6.0", + "@stylistic/eslint-plugin": "^2.11.0", + "@typescript-eslint/eslint-plugin": "^8.17.0", + "@typescript-eslint/parser": "^8.17.0", "clean-webpack-plugin": "^4.0.0", - "eslint": "^7.15.0", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-jsdoc": "^30.7.8", - "eslint-plugin-react": "^7.29.4", - "eslint-plugin-react-hooks": "^4.3.0", - "eslint-plugin-typescript-sort-keys": "^3.0.0", + "eslint": "^8.57.1", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsdoc": "^50.6.0", + "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-typescript-sort-keys": "^3.3.0", "jasmine-core": "^4.1.0", "karma": "^6.4.1", "karma-chrome-launcher": "^3.1.1", diff --git a/yarn.lock b/yarn.lock index 443374ac5b7..c4ddbd845df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,13 +2,6 @@ # yarn lockfile v1 -"@babel/code-frame@7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" - integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== - dependencies: - "@babel/highlight" "^7.10.4" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.22.13": version "7.22.13" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" @@ -147,7 +140,7 @@ "@babel/traverse" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/highlight@^7.10.4", "@babel/highlight@^7.22.13": +"@babel/highlight@^7.22.13": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== @@ -222,6 +215,15 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== +"@es-joy/jsdoccomment@~0.49.0": + version "0.49.0" + resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.49.0.tgz#e5ec1eda837c802eca67d3b29e577197f14ba1db" + integrity sha512-xjZTSFgECpb9Ohuk5yMX5RhUEbfeQcuOp8IF60e+wyzWEF0M5xeSgqsfLtvPEX8BIyOX9saZqzuGPmZ8oWc+5Q== + dependencies: + comment-parser "1.4.1" + esquery "^1.6.0" + jsdoc-type-pratt-parser "~4.1.0" + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -229,26 +231,31 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.5.1": - version "4.8.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.0.tgz#11195513186f68d42fbf449f9a7136b2c0c92005" - integrity sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg== +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== -"@eslint/eslintrc@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.0.tgz#99cc0a0584d72f1df38b900fb062ba995f395547" - integrity sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog== +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== dependencies: ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" - globals "^12.1.0" - ignore "^4.0.6" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" import-fresh "^3.2.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" + js-yaml "^4.1.0" + minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/js@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" + integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== + "@fortawesome/fontawesome-free@^5.6.3": version "5.9.0" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.9.0.tgz#1aa5c59efb1b8c6eb6277d1e3e8c8f31998b8c8e" @@ -259,6 +266,25 @@ resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-8.0.12.tgz#50aa8345d7f62402680c6d2d9814660761837001" integrity sha512-l3BiQRkD7qrnQv6ms6sqPLczvwbQpXt5iAVwjDvX0iumrz6yEonQkNAzNjeDX25/OJMFDTxpHjkJZHGpM9ikWw== +"@humanwhocodes/config-array@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" + integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== + dependencies: + "@humanwhocodes/object-schema" "^2.0.3" + debug "^4.3.1" + minimatch "^3.0.5" + +"@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/object-schema@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -369,11 +395,29 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@nodelib/fs.walk@^1.2.8": + 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" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== +"@pkgr/core@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" + integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== + +"@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== + "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -384,6 +428,17 @@ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== +"@stylistic/eslint-plugin@^2.11.0": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin/-/eslint-plugin-2.11.0.tgz#50d0289f36f7201055b7fa1729fdc1d8c46e93fa" + integrity sha512-PNRHbydNG5EH8NK4c+izdJlxajIR6GxcUhzsYNRsn6Myep4dsZt0qFCz3rCPnkvgO5FYibDcMqgNHUT+zvjYZw== + dependencies: + "@typescript-eslint/utils" "^8.13.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" + estraverse "^5.3.0" + picomatch "^4.0.2" + "@trysound/sax@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" @@ -720,7 +775,7 @@ dependencies: "@types/jquery" "*" -"@types/json-schema@^7.0.12", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.12" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== @@ -795,7 +850,7 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== -"@types/semver@^7.3.12", "@types/semver@^7.5.0": +"@types/semver@^7.3.12": version "7.5.1" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.1.tgz#0480eeb7221eb9bc398ad7432c9d7e14b1a5a367" integrity sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg== @@ -829,22 +884,20 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^6.6.0": - version "6.6.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.6.0.tgz#19ba09aa34fd504696445100262e5a9e1b1d7024" - integrity sha512-CW9YDGTQnNYMIo5lMeuiIG08p4E0cXrXTbcZ2saT/ETE7dWUrNxlijsQeU04qAAKkILiLzdQz+cGFxCJjaZUmA== - dependencies: - "@eslint-community/regexpp" "^4.5.1" - "@typescript-eslint/scope-manager" "6.6.0" - "@typescript-eslint/type-utils" "6.6.0" - "@typescript-eslint/utils" "6.6.0" - "@typescript-eslint/visitor-keys" "6.6.0" - debug "^4.3.4" +"@typescript-eslint/eslint-plugin@^8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.17.0.tgz#2ee073c421f4e81e02d10e731241664b6253b23c" + integrity sha512-HU1KAdW3Tt8zQkdvNoIijfWDMvdSweFYm4hWh+KwhPstv+sCmWb89hCIP8msFm9N1R/ooh9honpSuvqKWlYy3w== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.17.0" + "@typescript-eslint/type-utils" "8.17.0" + "@typescript-eslint/utils" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" graphemer "^1.4.0" - ignore "^5.2.4" + ignore "^5.3.1" natural-compare "^1.4.0" - semver "^7.5.4" - ts-api-utils "^1.0.1" + ts-api-utils "^1.3.0" "@typescript-eslint/experimental-utils@^5.0.0": version "5.62.0" @@ -853,15 +906,15 @@ dependencies: "@typescript-eslint/utils" "5.62.0" -"@typescript-eslint/parser@^6.6.0": - version "6.6.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.6.0.tgz#fe323a7b4eafb6d5ea82b96216561810394a739e" - integrity sha512-setq5aJgUwtzGrhW177/i+DMLqBaJbdwGj2CPIVFFLE0NCliy5ujIdLHd2D1ysmlmsjdL2GWW+hR85neEfc12w== +"@typescript-eslint/parser@^8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.17.0.tgz#2ee972bb12fa69ac625b85813dc8d9a5a053ff52" + integrity sha512-Drp39TXuUlD49F7ilHHCG7TTg8IkA+hxCuULdmzWYICxGXvDXmDmWEjJYZQYgf6l/TFfYNE167m7isnc3xlIEg== dependencies: - "@typescript-eslint/scope-manager" "6.6.0" - "@typescript-eslint/types" "6.6.0" - "@typescript-eslint/typescript-estree" "6.6.0" - "@typescript-eslint/visitor-keys" "6.6.0" + "@typescript-eslint/scope-manager" "8.17.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/typescript-estree" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" debug "^4.3.4" "@typescript-eslint/scope-manager@5.62.0": @@ -872,33 +925,33 @@ "@typescript-eslint/types" "5.62.0" "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/scope-manager@6.6.0": - version "6.6.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.6.0.tgz#57105d4419d6de971f7d2c30a2ff4ac40003f61a" - integrity sha512-pT08u5W/GT4KjPUmEtc2kSYvrH8x89cVzkA0Sy2aaOUIw6YxOIjA8ilwLr/1fLjOedX1QAuBpG9XggWqIIfERw== +"@typescript-eslint/scope-manager@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.17.0.tgz#a3f49bf3d4d27ff8d6b2ea099ba465ef4dbcaa3a" + integrity sha512-/ewp4XjvnxaREtqsZjF4Mfn078RD/9GmiEAtTeLQ7yFdKnqwTOgRMSvFz4et9U5RiJQ15WTGXPLj89zGusvxBg== dependencies: - "@typescript-eslint/types" "6.6.0" - "@typescript-eslint/visitor-keys" "6.6.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" -"@typescript-eslint/type-utils@6.6.0": - version "6.6.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.6.0.tgz#14f651d13b884915c4fca0d27adeb652a4499e86" - integrity sha512-8m16fwAcEnQc69IpeDyokNO+D5spo0w1jepWWY2Q6y5ZKNuj5EhVQXjtVAeDDqvW6Yg7dhclbsz6rTtOvcwpHg== +"@typescript-eslint/type-utils@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.17.0.tgz#d326569f498cdd0edf58d5bb6030b4ad914e63d3" + integrity sha512-q38llWJYPd63rRnJ6wY/ZQqIzPrBCkPdpIsaCfkR3Q4t3p6sb422zougfad4TFW9+ElIFLVDzWGiGAfbb/v2qw== dependencies: - "@typescript-eslint/typescript-estree" "6.6.0" - "@typescript-eslint/utils" "6.6.0" + "@typescript-eslint/typescript-estree" "8.17.0" + "@typescript-eslint/utils" "8.17.0" debug "^4.3.4" - ts-api-utils "^1.0.1" + ts-api-utils "^1.3.0" "@typescript-eslint/types@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/types@6.6.0": - version "6.6.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.6.0.tgz#95e7ea650a2b28bc5af5ea8907114a48f54618c2" - integrity sha512-CB6QpJQ6BAHlJXdwUmiaXDBmTqIE2bzGTDLADgvqtHWuhfNP3rAOK7kAgRMAET5rDRr9Utt+qAzRBdu3AhR3sg== +"@typescript-eslint/types@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.17.0.tgz#ef84c709ef8324e766878834970bea9a7e3b72cf" + integrity sha512-gY2TVzeve3z6crqh2Ic7Cr+CAv6pfb0Egee7J5UAVWCpVvDI/F71wNfolIim4FE6hT15EbpZFVUj9j5i38jYXA== "@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" @@ -913,18 +966,19 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@6.6.0": - version "6.6.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.6.0.tgz#373c420d2e12c28220f4a83352280a04823a91b7" - integrity sha512-hMcTQ6Al8MP2E6JKBAaSxSVw5bDhdmbCEhGW/V8QXkb9oNsFkA4SBuOMYVPxD3jbtQ4R/vSODBsr76R6fP3tbA== +"@typescript-eslint/typescript-estree@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.17.0.tgz#40b5903bc929b1e8dd9c77db3cb52cfb199a2a34" + integrity sha512-JqkOopc1nRKZpX+opvKqnM3XUlM7LpFMD0lYxTqOTKQfCWAmxw45e3qlOCsEqEB2yuacujivudOFpCnqkBDNMw== dependencies: - "@typescript-eslint/types" "6.6.0" - "@typescript-eslint/visitor-keys" "6.6.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" debug "^4.3.4" - globby "^11.1.0" + fast-glob "^3.3.2" is-glob "^4.0.3" - semver "^7.5.4" - ts-api-utils "^1.0.1" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" "@typescript-eslint/utils@5.62.0": version "5.62.0" @@ -940,18 +994,15 @@ eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/utils@6.6.0": - version "6.6.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.6.0.tgz#2d686c0f0786da6362d909e27a9de1c13ba2e7dc" - integrity sha512-mPHFoNa2bPIWWglWYdR0QfY9GN0CfvvXX1Sv6DlSTive3jlMTUy+an67//Gysc+0Me9pjitrq0LJp0nGtLgftw== +"@typescript-eslint/utils@8.17.0", "@typescript-eslint/utils@^8.13.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.17.0.tgz#41c05105a2b6ab7592f513d2eeb2c2c0236d8908" + integrity sha512-bQC8BnEkxqG8HBGKwG9wXlZqg37RKSMY7v/X8VEWD8JG2JuTHuNK0VFvMPMUKQcbk6B+tf05k+4AShAEtCtJ/w== dependencies: "@eslint-community/eslint-utils" "^4.4.0" - "@types/json-schema" "^7.0.12" - "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.6.0" - "@typescript-eslint/types" "6.6.0" - "@typescript-eslint/typescript-estree" "6.6.0" - semver "^7.5.4" + "@typescript-eslint/scope-manager" "8.17.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/typescript-estree" "8.17.0" "@typescript-eslint/visitor-keys@5.62.0": version "5.62.0" @@ -961,13 +1012,18 @@ "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@6.6.0": - version "6.6.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.6.0.tgz#1109088b4346c8b2446f3845db526374d9a3bafc" - integrity sha512-L61uJT26cMOfFQ+lMZKoJNbAEckLe539VhTxiGHrWl5XSKQgA0RTBZJW2HFPy5T0ZvPVSD93QsrTKDkfNwJGyQ== +"@typescript-eslint/visitor-keys@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.17.0.tgz#4dbcd0e28b9bf951f4293805bf34f98df45e1aa8" + integrity sha512-1Hm7THLpO6ww5QU6H/Qp+AusUUl+z/CAm3cNZZ0jQvon9yicgO7Rwd+/WWRpMKLYV6p2UvdbR27c86rzCPpreg== dependencies: - "@typescript-eslint/types" "6.6.0" - eslint-visitor-keys "^3.4.1" + "@typescript-eslint/types" "8.17.0" + eslint-visitor-keys "^4.2.0" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== "@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": version "1.12.1" @@ -1128,26 +1184,26 @@ acorn-import-attributes@^1.9.5: resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== -acorn-jsx@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" - integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== +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-walk@^8.0.2: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^7.4.0: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - acorn@^8.1.0, acorn@^8.7.1, acorn@^8.8.2: version "8.10.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== +acorn@^8.14.0, acorn@^8.9.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + ajv-formats@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" @@ -1167,7 +1223,7 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1177,16 +1233,6 @@ ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^7.0.2: - version "7.2.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.2.1.tgz#a5ac226171912447683524fa2f1248fcf8bac83d" - integrity sha512-+nu0HDv7kNSOua9apAVc979qd932rrZeb3WOvoiD31A/p1mIE5/9bN2027pE2rOPYEdS3UHzsvof4hY+lM9/WQ== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - ajv@^8.0.0, ajv@^8.9.0: version "8.12.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" @@ -1197,11 +1243,6 @@ ajv@^8.0.0, ajv@^8.9.0: require-from-string "^2.0.2" uri-js "^4.2.2" -ansi-colors@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -1249,12 +1290,10 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" +are-docs-informative@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/are-docs-informative/-/are-docs-informative-0.0.2.tgz#387f0e93f5d45280373d387a59d34c96db321963" + integrity sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig== argparse@^2.0.1: version "2.0.1" @@ -1276,7 +1315,15 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== -array-includes@^3.1.2, array-includes@^3.1.3, array-includes@^3.1.4: +array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + +array-includes@^3.1.2: version "3.1.4" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== @@ -1287,6 +1334,18 @@ array-includes@^3.1.2, array-includes@^3.1.3, array-includes@^3.1.4: get-intrinsic "^1.1.1" is-string "^1.0.7" +array-includes@^3.1.8: + version "3.1.8" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" + integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + is-string "^1.0.7" + array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -1309,23 +1368,74 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== -array.prototype.flat@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz#6ef638b43312bd401b4c6199fdec7e2dc9e9a123" - integrity sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg== +array.prototype.findlast@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" + integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.1" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" -array.prototype.flatmap@^1.2.5: +array.prototype.findlastindex@^1.2.5: version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz#908dc82d8a406930fdf38598d51e7411d18d4446" - integrity sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA== + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" + integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - es-abstract "^1.19.0" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" + +array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.tosorted@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" + integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.3" + es-errors "^1.3.0" + es-shim-unscopables "^1.0.2" + +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" arrify@^1.0.1: version "1.0.1" @@ -1357,11 +1467,6 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - async@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/async/-/async-3.1.0.tgz#42b3b12ae1b74927b5217d8c0016baaf62463772" @@ -1389,6 +1494,13 @@ autosize@5.0.2: resolved "https://registry.yarnpkg.com/autosize/-/autosize-5.0.2.tgz#0a327c200dda4a1742817e94dcb8af5700685689" integrity sha512-FPVt5ynkqUAA9gcMZnJHka1XfQgr1WNd/yRfIjmj5WGmjua+u5Hl9hn8M2nU5CNy2bEIcj1ZUwXq7IOHsfZG9w== +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" + bail@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" @@ -1658,7 +1770,7 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.7: +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== @@ -1917,10 +2029,10 @@ 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== -comment-parser@^0.7.6: - version "0.7.6" - resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.7.6.tgz#0e743a53c8e646c899a1323db31f6cd337b10f12" - integrity sha512-GKNxVA7/iuTnAqGADlTWX4tkhzxZKXp5fLJqKTlQLHkE65XDUKutZ3BHaJC5IGcper2tT3QRD1xr4o3jNpgXXg== +comment-parser@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.4.1.tgz#bdafead37961ac079be11eb7ec65c4d021eaf9cc" + integrity sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg== component-emitter@^1.2.1: version "1.3.0" @@ -2485,12 +2597,39 @@ d3@^7.1.1: d3-transition "3" d3-zoom "3" +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + date-format@^4.0.14: version "4.0.14" resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -2504,13 +2643,20 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: +debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: version "4.3.5" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== dependencies: ms "2.1.2" +debug@^4.3.2, debug@^4.3.6: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -2545,7 +2691,7 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-data-property@^1.1.4: +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== @@ -2561,6 +2707,15 @@ define-properties@^1.1.1, define-properties@^1.1.3: dependencies: object-keys "^1.0.12" +define-properties@^1.2.0, 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" + define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" @@ -2826,13 +2981,6 @@ enhanced-resolve@^5.0.0, enhanced-resolve@^5.17.1, enhanced-resolve@^5.7.0: graceful-fs "^4.2.4" tapable "^2.2.0" -enquirer@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" - integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== - dependencies: - ansi-colors "^4.1.1" - ent@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" @@ -2862,7 +3010,59 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.18.0-next.1, es-abstract@^1.19.0, es-abstract@^1.19.1: +es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5: + version "1.23.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.5.tgz#f4599a4946d57ed467515ed10e4f157289cd52fb" + integrity sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" + is-callable "^1.2.7" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.3" + object-keys "^1.1.1" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.3" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + +es-abstract@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== @@ -2895,16 +3095,65 @@ es-define-property@^1.0.0: dependencies: get-intrinsic "^1.2.4" -es-errors@^1.3.0: +es-errors@^1.2.1, 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-iterator-helpers@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz#2f1a3ab998b30cb2d10b195b587c6d9ebdebf152" + integrity sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.3" + es-errors "^1.3.0" + es-set-tostringtag "^2.0.3" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + globalthis "^1.0.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + iterator.prototype "^1.1.3" + safe-array-concat "^1.1.2" + es-module-lexer@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.0.tgz#6be9c9e0b4543a60cd166ff6f8b4e9dae0b0c16f" integrity sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA== +es-module-lexer@^1.5.3: + version "1.5.4" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" + integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== + +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" + +es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -2934,90 +3183,107 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +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== + escape-string-regexp@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== -eslint-import-resolver-node@^0.3.4: - version "0.3.4" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" - integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== +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 "^2.6.9" - resolve "^1.13.1" + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" -eslint-module-utils@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz#b51be1e473dd0de1c5ea638e22429c2490ea8233" - integrity sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A== +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" - pkg-dir "^2.0.0" -eslint-plugin-import@^2.22.1: - version "2.23.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz#8dceb1ed6b73e46e50ec9a5bb2411b645e7d3d97" - integrity sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ== +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: - array-includes "^3.1.3" - array.prototype.flat "^1.2.4" - debug "^2.6.9" + "@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.4" - eslint-module-utils "^2.6.1" - find-up "^2.0.0" - has "^1.0.3" - is-core-module "^2.4.0" - minimatch "^3.0.4" - object.values "^1.1.3" - pkg-up "^2.0.0" - read-pkg-up "^3.0.0" - resolve "^1.20.0" - tsconfig-paths "^3.9.0" - -eslint-plugin-jsdoc@^30.7.8: - version "30.7.13" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-30.7.13.tgz#52e5c74fb806d3bbeb51d04a0c829508c3c6b563" - integrity sha512-YM4WIsmurrp0rHX6XiXQppqKB8Ne5ATiZLJe2+/fkp9l9ExXFr43BbAbjZaVrpCT+tuPYOZ8k1MICARHnURUNQ== - dependencies: - comment-parser "^0.7.6" - debug "^4.3.1" - jsdoctypeparser "^9.0.0" - lodash "^4.17.20" - regextras "^0.7.1" - semver "^7.3.4" - spdx-expression-parse "^3.0.1" - -eslint-plugin-react-hooks@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172" - integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA== - -eslint-plugin-react@^7.29.4: - version "7.29.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz#4717de5227f55f3801a5fd51a16a4fa22b5914d2" - integrity sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ== - dependencies: - array-includes "^3.1.4" - array.prototype.flatmap "^1.2.5" + 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-plugin-jsdoc@^50.6.0: + version "50.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.6.0.tgz#2c6049a40305313174a30212bc360e775b797a0a" + integrity sha512-tCNp4fR79Le3dYTPB0dKEv7yFyvGkUCa+Z3yuTrrNGGOxBlXo9Pn0PEgroOZikUQOGjxoGMVKNjrOHcYEdfszg== + dependencies: + "@es-joy/jsdoccomment" "~0.49.0" + are-docs-informative "^0.0.2" + comment-parser "1.4.1" + debug "^4.3.6" + escape-string-regexp "^4.0.0" + espree "^10.1.0" + esquery "^1.6.0" + parse-imports "^2.1.1" + semver "^7.6.3" + spdx-expression-parse "^4.0.0" + synckit "^0.9.1" + +eslint-plugin-react-hooks@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz#72e2eefbac4b694f5324154619fee44f5f60f101" + integrity sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw== + +eslint-plugin-react@^7.37.2: + version "7.37.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz#cd0935987876ba2900df2f58339f6d92305acc7a" + integrity sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w== + dependencies: + array-includes "^3.1.8" + array.prototype.findlast "^1.2.5" + array.prototype.flatmap "^1.3.2" + array.prototype.tosorted "^1.1.4" doctrine "^2.1.0" + es-iterator-helpers "^1.1.0" estraverse "^5.3.0" + hasown "^2.0.2" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" - object.entries "^1.1.5" - object.fromentries "^2.0.5" - object.hasown "^1.1.0" - object.values "^1.1.5" + object.entries "^1.1.8" + object.fromentries "^2.0.8" + object.values "^1.2.0" prop-types "^15.8.1" - resolve "^2.0.0-next.3" - semver "^6.3.0" - string.prototype.matchall "^4.0.6" + resolve "^2.0.0-next.5" + semver "^6.3.1" + string.prototype.matchall "^4.0.11" + string.prototype.repeat "^1.0.0" -eslint-plugin-typescript-sort-keys@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-typescript-sort-keys/-/eslint-plugin-typescript-sort-keys-3.0.0.tgz#ebec17d2647d1e078c669c726aff04b26170d181" - integrity sha512-bMmI4prYlf3l/1O8j8Nsz11m+XfKEHRFk9aJqP91L4Hgy7I38lnitnYElDmPQaznE1oFlGgBcnkEizNT2NLylQ== +eslint-plugin-typescript-sort-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-typescript-sort-keys/-/eslint-plugin-typescript-sort-keys-3.3.0.tgz#a3ba29e5d6b7f3e5e66ad898347fa9142d3596d1" + integrity sha512-bRW3Rc/VNdrSP9OoY5wgjjaXCOOkZKpzvl/Mk6l8Sg8CMehVIcg9K4y33l+ZcZiknpl0aR6rKusxuCJNGZWmVw== dependencies: "@typescript-eslint/experimental-utils" "^5.0.0" json-schema "^0.4.0" @@ -3031,89 +3297,90 @@ eslint-scope@5.1.1, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + esrecurse "^4.3.0" + estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: +eslint-visitor-keys@^3.3.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@^7.15.0: - version "7.22.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.22.0.tgz#07ecc61052fec63661a2cab6bd507127c07adc6f" - integrity sha512-3VawOtjSJUQiiqac8MQc+w457iGLfuNGLFn8JmF051tTKbh5/x/0vlcEj8OgDCaw7Ysa2Jn8paGshV7x2abKXg== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@^8.57.1: + version "8.57.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" + integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.0" - ajv "^6.10.0" + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.1" + "@humanwhocodes/config-array" "^0.13.0" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" - debug "^4.0.1" + debug "^4.3.2" doctrine "^3.0.0" - enquirer "^2.3.5" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" - esquery "^1.4.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" esutils "^2.0.2" + fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.0.0" - globals "^13.6.0" - ignore "^4.0.6" - import-fresh "^3.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - js-yaml "^3.13.1" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.21" - minimatch "^3.0.4" + lodash.merge "^4.6.2" + minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" - strip-json-comments "^3.1.0" - table "^6.0.4" + optionator "^0.9.3" + strip-ansi "^6.0.1" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^7.3.0, espree@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" - integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== +espree@^10.1.0, espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== dependencies: - acorn "^7.4.0" - acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" -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== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" -esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== +esquery@^1.4.2, esquery@^1.6.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" @@ -3250,6 +3517,17 @@ fast-glob@^3.2.11, fast-glob@^3.2.9, fast-glob@^3.3.0: merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + 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.4" + 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" @@ -3314,13 +3592,6 @@ finalhandler@1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-up@^2.0.0, find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -3336,6 +3607,14 @@ find-up@^4.0.0: locate-path "^5.0.0" path-exists "^4.0.0" +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@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -3354,6 +3633,13 @@ follow-redirects@^1.0.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -3435,10 +3721,20 @@ function-bind@^1.1.1, function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +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== gensync@^1.0.0-beta.1: version "1.0.0-beta.1" @@ -3455,7 +3751,7 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: +get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== @@ -3481,6 +3777,15 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== + dependencies: + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -3494,14 +3799,14 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0, glob-parent@^5.1.2, glob-parent@~5.1.2: +glob-parent@^5.1.2, 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.1: +glob-parent@^6.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== @@ -3546,19 +3851,20 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^12.1.0: - version "12.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" - integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== dependencies: - type-fest "^0.8.1" + type-fest "^0.20.2" -globals@^13.6.0: - version "13.6.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.6.0.tgz#d77138e53738567bb96a3916ff6f6b487af20ef7" - integrity sha512-YFKCX0SiPg7l5oKYCJ2zZGxcXprVXHcSnVuvzrT3oSENQonVLqM5pf9fN5dLGZGyCjhw8TN8Btwe/jKnZ0pjvQ== +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: - type-fest "^0.20.2" + define-properties "^1.2.1" + gopd "^1.0.1" globby@^11.1.0: version "11.1.0" @@ -3634,6 +3940,11 @@ has-bigints@^1.0.1: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -3644,7 +3955,7 @@ has-flag@^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.2: +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== @@ -3656,6 +3967,13 @@ has-proto@^1.0.1: resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== +has-proto@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.1.0.tgz#deb10494cbbe8809bce168a3b961f42969f5ed43" + integrity sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q== + dependencies: + call-bind "^1.0.7" + has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" @@ -3668,6 +3986,13 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" +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" + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -3723,7 +4048,7 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" -hasown@^2.0.0: +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== @@ -3749,11 +4074,6 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -3813,16 +4133,16 @@ ignore@^3.3.5: resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - ignore@^5.2.0, ignore@^5.2.4: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== +ignore@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + image-size@~0.5.0: version "0.5.5" resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" @@ -3852,7 +4172,7 @@ immer@^9.0.6: resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.15.tgz#0b9169e5b1d22137aba7d43f8a81a495dd1b62dc" integrity sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ== -import-fresh@^3.0.0, import-fresh@^3.2.1, import-fresh@^3.3.0: +import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -3920,6 +4240,15 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" +internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + "internmap@1 - 2": version "2.0.3" resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" @@ -3967,11 +4296,26 @@ is-arguments@^1.0.4: resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-async-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" + integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== + dependencies: + has-tostringtag "^1.0.0" + is-bigint@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" @@ -4001,12 +4345,24 @@ is-buffer@^2.0.0: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== +is-callable@^1.1.3, 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-callable@^1.1.4, is-callable@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== -is-core-module@^2.2.0, is-core-module@^2.4.0, is-core-module@^2.9.0: +is-core-module@^2.13.0, is-core-module@^2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== + dependencies: + hasown "^2.0.2" + +is-core-module@^2.9.0: version "2.11.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== @@ -4027,11 +4383,25 @@ is-data-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" + is-date-object@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== +is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + is-decimal@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" @@ -4077,6 +4447,13 @@ is-extglob@^2.1.0, is-extglob@^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.0" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.0.tgz#d74a7d0c5f3578e34a20729e69202e578d495dc2" + integrity sha512-qfMdqbAQEwBw78ZyReKnlA8ezmPdb9BemzIIip/JkjaZUhitfXDkkr+3QTboW0JrSXT1QWyYShpvnNHGZ4c4yA== + dependencies: + call-bind "^1.0.7" + is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" @@ -4094,6 +4471,13 @@ is-fullwidth-code-point@^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.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-generator-function@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" @@ -4123,6 +4507,11 @@ is-hotkey@^0.1.6: resolved "https://registry.yarnpkg.com/is-hotkey/-/is-hotkey-0.1.6.tgz#c214b1ccdcbda46fba4ba93d2de64915db737471" integrity sha512-1+hMr0GLPM0M49UDRt9RgE8i+SM29UY4AGRP6sGz6fThOVXqSrEvTMakolhHMcVizJnPNAoMpEmE+Oi1k2NrZQ== +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-nan@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.2.1.tgz#9faf65b6fb6db24b7f5c0628475ea71f988401e2" @@ -4135,6 +4524,11 @@ is-negative-zero@^2.0.1: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== +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.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" @@ -4171,6 +4565,11 @@ is-path-inside@^2.1.0: dependencies: path-is-inside "^1.0.2" +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" @@ -4189,11 +4588,23 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +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.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4213,13 +4624,33 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-weakref@^1.0.1: +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +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.1, is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== dependencies: call-bind "^1.0.2" +is-weakset@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007" + integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + is-what@^3.12.0: version "3.14.1" resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1" @@ -4242,6 +4673,11 @@ isarray@1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== +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== + isbinaryfile@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.8.tgz#5d34b94865bd4946633ecc78a026fc76c5b11fcf" @@ -4308,6 +4744,17 @@ istanbul-reports@^3.0.0: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +iterator.prototype@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.3.tgz#016c2abe0be3bbdb8319852884f60908ac62bf9c" + integrity sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ== + dependencies: + define-properties "^1.2.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + reflect.getprototypeof "^1.0.4" + set-function-name "^2.0.1" + jackspeak@^2.0.3: version "2.3.3" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.3.tgz#95e4cbcc03b3eb357bf6bcce14a903fb3d1151e1" @@ -4397,14 +4844,6 @@ jquery.scrollto@^2.1.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -4412,21 +4851,16 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsdoctypeparser@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/jsdoctypeparser/-/jsdoctypeparser-9.0.0.tgz#8c97e2fb69315eb274b0f01377eaa5c940bd7b26" - integrity sha512-jrTA2jJIL6/DAEILBEh2/w9QxCuwmvNXIry39Ay/HVfhE3o2yVV0U44blYkqdHA/OKloJEqvJy0xU+GSdE2SIw== +jsdoc-type-pratt-parser@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz#ff6b4a3f339c34a6c188cbf50a16087858d22113" + integrity sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg== jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -json-parse-better-errors@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -4457,7 +4891,7 @@ json-stringify-safe@^5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json5@^1.0.1: +json5@^1.0.1, 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== @@ -4702,16 +5136,6 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - loader-runner@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" @@ -4726,14 +5150,6 @@ loader-utils@^1.0.2: emojis-list "^3.0.0" json5 "^1.0.1" -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -4749,6 +5165,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.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.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -4759,12 +5182,17 @@ lodash.memoize@~3.0.3: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" integrity sha1-LcvSwofLwKVcxCMovQxzYVDVPj8= +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.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4: +lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -5288,7 +5716,7 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.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== @@ -5302,7 +5730,14 @@ minimatch@^9.0.1: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.5: +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -5364,7 +5799,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1: +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== @@ -5443,16 +5878,6 @@ node-releases@^2.0.13: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== -normalize-package-data@^2.3.2: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -5506,6 +5931,11 @@ object-inspect@^1.11.0, object-inspect@^1.13.1: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== +object-inspect@^1.13.3: + version "1.13.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.3.tgz#f14c183de51130243d6d18ae149375ff50ea488a" + integrity sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA== + object-is@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6" @@ -5533,31 +5963,43 @@ object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" -object.entries@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" - integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== +object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" -object.fromentries@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" - integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== +object.entries@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" + integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" -object.hasown@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.0.tgz#7232ed266f34d197d15cac5880232f7a4790afe5" - integrity sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg== +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: - define-properties "^1.1.3" - es-abstract "^1.19.1" + 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.pick@^1.3.0: version "1.3.0" @@ -5566,14 +6008,14 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.3, object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== +object.values@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" + integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" on-finished@2.4.1: version "2.4.1" @@ -5596,17 +6038,17 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +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.3" + word-wrap "^1.2.5" os-browserify@^0.3.0: version "0.3.0" @@ -5637,13 +6079,6 @@ p-is-promise@^2.0.0: resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -5651,12 +6086,12 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= +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: - p-limit "^1.1.0" + yocto-queue "^0.1.0" p-locate@^3.0.0: version "3.0.0" @@ -5672,6 +6107,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.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" + p-map@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" @@ -5682,11 +6124,6 @@ p-pipe@^1.1.0: resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9" integrity sha1-SxoROZoRUgpneQ7loMHViB1r7+k= -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -5734,13 +6171,13 @@ parse-entities@^2.0.0: is-decimal "^1.0.0" is-hexadecimal "^1.0.0" -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= +parse-imports@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/parse-imports/-/parse-imports-2.2.1.tgz#0a6e8b5316beb5c9905f50eb2bbb8c64a4805642" + integrity sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ== dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" + es-module-lexer "^1.5.3" + slashes "^3.0.12" parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" @@ -5802,7 +6239,7 @@ path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6, path-parse@^1.0.7: +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== @@ -5853,6 +6290,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^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" + integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -5880,13 +6322,6 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== -pkg-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" - integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= - dependencies: - find-up "^2.1.0" - pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -5894,18 +6329,16 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" - integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= - dependencies: - find-up "^2.1.0" - posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + postcss-calc@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-9.0.1.tgz#a744fd592438a93d6de0f1434c572670361eb6c6" @@ -6176,11 +6609,6 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - prop-types@^15.0.0, prop-types@^15.6.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" @@ -6357,23 +6785,6 @@ react@^17.0.2: loose-envify "^1.1.0" object-assign "^4.1.1" -read-pkg-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" - integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= - dependencies: - find-up "^2.0.0" - read-pkg "^3.0.0" - -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.5.0, readable-stream@^3.6.0, readable-stream@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" @@ -6397,6 +6808,19 @@ rechoir@^0.8.0: dependencies: resolve "^1.20.0" +reflect.getprototypeof@^1.0.4, reflect.getprototypeof@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.7.tgz#04311b33a1b713ca5eb7b5aed9950a86481858e5" + integrity sha512-bMvFGIUKlc/eSfXNX+aZ+EL95/EgZzuwA0OBPTbZZDEJw/0AkentjMuM1oiRfwHrshqk4RzdgiTg5CcDalXN5g== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + which-builtin-type "^1.1.4" + regenerator-runtime@^0.13.11: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" @@ -6410,23 +6834,15 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" - integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA== +regexp.prototype.flags@^1.5.2, regexp.prototype.flags@^1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42" + integrity sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -regexpp@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - -regextras@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.7.1.tgz#be95719d5f43f9ef0b9fa07ad89b7c606995a3b2" - integrity sha512-9YXf6xtW+qzQ+hcMQXx95MOvfqXFgsKDZodX3qZB0x2n5Z94ioetIITsBtvJbiOyxa/6s9AtyweBLCdPmPko/w== + call-bind "^1.0.7" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.2" rehype-truncate@^1.2.2: version "1.2.2" @@ -6527,7 +6943,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.20.0, resolve@^1.3.2: +resolve@^1.17.0, resolve@^1.20.0, resolve@^1.3.2: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -6536,13 +6952,23 @@ resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.20.0, resolve@^1.3 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^2.0.0-next.3: - version "2.0.0-next.3" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" - integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q== +resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^2.0.0-next.5: + version "2.0.0-next.5" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" ret@~0.1.10: version "0.1.15" @@ -6605,6 +7031,16 @@ sade@^1.7.3: dependencies: mri "^1.1.0" +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -6615,6 +7051,15 @@ safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-regex "^1.1.4" + safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -6661,23 +7106,28 @@ scroll-into-view-if-needed@^2.2.20: dependencies: compute-scroll-into-view "^1.0.17" -"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: +semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.0.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.3.0, 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.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.4: +semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" +semver@^7.6.0, semver@^7.6.3: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" @@ -6702,6 +7152,16 @@ set-function-length@^1.2.1: gopd "^1.0.1" has-property-descriptors "^1.0.2" +set-function-name@^2.0.1, 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-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -6806,6 +7266,11 @@ slash@^4.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== +slashes@^3.0.12: + version "3.0.12" + resolved "https://registry.yarnpkg.com/slashes/-/slashes-3.0.12.tgz#3d664c877ad542dc1509eaf2c50f38d483a6435a" + integrity sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA== + slate-history@^0.66.0: version "0.66.0" resolved "https://registry.yarnpkg.com/slate-history/-/slate-history-0.66.0.tgz#ac63fddb903098ceb4c944433e3f75fe63acf940" @@ -6836,15 +7301,6 @@ slate@^0.82.1: is-plain-object "^5.0.0" tiny-warning "^1.0.3" -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -6958,23 +7414,15 @@ space-separated-tokens@^2.0.0: resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - spdx-exceptions@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== -spdx-expression-parse@^3.0.0, spdx-expression-parse@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== +spdx-expression-parse@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz#a23af9f3132115465dac215c099303e4ceac5794" + integrity sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" @@ -6991,11 +7439,6 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -7085,19 +7528,41 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string.prototype.matchall@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz#5abb5dabc94c7b0ea2380f65ba610b3a544b15fa" - integrity sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg== +string.prototype.matchall@^4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" + integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + regexp.prototype.flags "^1.5.2" + set-function-name "^2.0.2" + side-channel "^1.0.6" + +string.prototype.repeat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" + integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== dependencies: - call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" - has-symbols "^1.0.2" - internal-slot "^1.0.3" - regexp.prototype.flags "^1.3.1" - side-channel "^1.0.4" + es-abstract "^1.17.5" + +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" string.prototype.trimend@^1.0.4: version "1.0.4" @@ -7107,6 +7572,15 @@ string.prototype.trimend@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + string.prototype.trimstart@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" @@ -7115,6 +7589,15 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +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" + string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -7172,7 +7655,7 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +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== @@ -7230,15 +7713,13 @@ svgo@^3.0.2: csso "^5.0.5" picocolors "^1.0.0" -table@^6.0.4: - version "6.0.7" - resolved "https://registry.yarnpkg.com/table/-/table-6.0.7.tgz#e45897ffbcc1bcf9e8a87bf420f2c9e5a7a52a34" - integrity sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g== +synckit@^0.9.1: + version "0.9.2" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.9.2.tgz#a3a935eca7922d48b9e7d6c61822ee6c3ae4ec62" + integrity sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw== dependencies: - ajv "^7.0.2" - lodash "^4.17.20" - slice-ansi "^4.0.0" - string-width "^4.2.0" + "@pkgr/core" "^0.1.0" + tslib "^2.6.2" tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: version "2.2.1" @@ -7363,10 +7844,10 @@ trough@^2.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-2.1.0.tgz#0f7b511a4fde65a46f18477ab38849b22c554876" integrity sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g== -ts-api-utils@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" - integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== +ts-api-utils@^1.3.0: + 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-loader@^9.4.4: version "9.4.4" @@ -7387,6 +7868,16 @@ tsconfig-paths-webpack-plugin@^3.2.0: enhanced-resolve "^5.7.0" tsconfig-paths "^3.9.0" +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" + tsconfig-paths@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" @@ -7402,6 +7893,11 @@ tslib@^1.10.0, tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.6.2: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -7431,11 +7927,6 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -7444,6 +7935,51 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-byte-offset@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.3.tgz#3fa9f22567700cc86aaf86a1e7176f74b59600f2" + integrity sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + reflect.getprototypeof "^1.0.6" + +typed-array-length@^1.0.6: + 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.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" @@ -7464,6 +8000,16 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + unified@^10.0.0, unified@^10.1.2: version "10.1.2" resolved "https://registry.yarnpkg.com/unified/-/unified-10.1.2.tgz#b1d64e55dafe1f0b98bb6c719881103ecf6c86df" @@ -7631,19 +8177,6 @@ uvu@^0.5.0: kleur "^4.0.3" sade "^1.7.3" -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - vary@^1: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -7795,11 +8328,51 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-builtin-type@^1.1.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.0.tgz#58042ac9602d78a6d117c7e811349df1268ba63c" + integrity sha512-I+qLGQ/vucCby4tf5HsLmGueEla4ZhwTBSqaooS+Y0BuxN4Cp+okmGuV+8mXZ84KDI9BA+oklo+RzKg0ONdSUA== + dependencies: + call-bind "^1.0.7" + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" + is-async-function "^2.0.0" + is-date-object "^1.0.5" + is-finalizationregistry "^1.1.0" + is-generator-function "^1.0.10" + is-regex "^1.1.4" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.2" + which-typed-array "^1.1.15" + +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-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which-typed-array@^1.1.14, which-typed-array@^1.1.15: + version "1.1.16" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.16.tgz#db4db429c4706feca2f01677a144278e4a8c216b" + integrity sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + which@^1.2.1, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -7819,10 +8392,10 @@ wildcard@^2.0.0: resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== -word-wrap@^1.2.3: - version "1.2.4" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" - integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== +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" @@ -7938,6 +8511,11 @@ yargs@^16.1.1: y18n "^5.0.5" yargs-parser "^20.2.2" +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== + ziggy-js@^1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/ziggy-js/-/ziggy-js-1.8.1.tgz#582f4c19424cec0f8b80de2d033d70f953314a94" From 1e2e5c3bee5ec3f220532259515cd623ec559ce8 Mon Sep 17 00:00:00 2001 From: agatemosu Date: Thu, 5 Dec 2024 13:29:51 +0100 Subject: [PATCH 002/153] Migrate rules --- .eslintrc.js | 102 +++++++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b1b82651674..a82f827ccf5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -25,6 +25,8 @@ module.exports = { 'typescript-sort-keys', ], rules: { + '@stylistic/member-delimiter-style': 'error', + '@stylistic/type-annotation-spacing': 'error', '@typescript-eslint/array-type': [ 'error', { @@ -41,20 +43,6 @@ module.exports = { }, ], '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/indent': [ - 'error', - 2, - { - FunctionDeclaration: { - parameters: 'first', - }, - FunctionExpression: { - parameters: 'first', - }, - SwitchCase: 1, - }, - ], - '@typescript-eslint/member-delimiter-style': 'error', '@typescript-eslint/member-ordering': [ 'error', { @@ -101,17 +89,17 @@ module.exports = { '@typescript-eslint/no-unsafe-member-access': 'warn', '@typescript-eslint/no-unsafe-return': 'warn', '@typescript-eslint/no-unused-expressions': 'error', - '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', ignoreRestSiblings: true }], + '@typescript-eslint/no-unused-vars': [ + 'error', { + argsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_', + ignoreRestSiblings: true, + }, + ], '@typescript-eslint/no-use-before-define': 'off', - '@typescript-eslint/object-curly-spacing': ['error', 'always'], '@typescript-eslint/prefer-for-of': 'error', '@typescript-eslint/prefer-function-type': 'error', '@typescript-eslint/prefer-readonly': 'error', - '@typescript-eslint/quotes': [ - 'error', - 'single', - { avoidEscape: true }, - ], '@typescript-eslint/restrict-template-expressions': [ 'error', { @@ -121,7 +109,6 @@ module.exports = { allowNumber: true, }, ], - '@typescript-eslint/semi': ['error', 'always'], // TODO: make more strict. '@typescript-eslint/strict-boolean-expressions': [ 'error', @@ -129,7 +116,6 @@ module.exports = { allowNullableBoolean: true, }, ], - '@typescript-eslint/type-annotation-spacing': 'error', '@typescript-eslint/unified-signatures': 'error', 'dot-notation': 'off', 'no-invalid-this': 'off', @@ -190,22 +176,57 @@ module.exports = { sourceType: 'module', }, plugins: [ + '@stylistic', 'eslint-plugin-jsdoc', 'eslint-plugin-import', ], rules: { + '@stylistic/arrow-parens': 'error', + '@stylistic/arrow-spacing': 'error', + '@stylistic/brace-style': 'error', + '@stylistic/comma-dangle': ['error', 'always-multiline'], + '@stylistic/eol-last': 'error', + '@stylistic/indent': [ + 'error', + 2, + { + FunctionDeclaration: { + parameters: 'first', + }, + FunctionExpression: { + parameters: 'first', + }, + SwitchCase: 1, + }, + ], + '@stylistic/max-len': 'off', + '@stylistic/new-parens': 'error', + '@stylistic/no-multiple-empty-lines': 'error', + '@stylistic/no-trailing-spaces': 'error', + '@stylistic/object-curly-spacing': ['error', 'always'], + '@stylistic/quote-props': ['error', 'as-needed'], + '@stylistic/quotes': [ + 'error', + 'single', + { avoidEscape: true }, + ], + '@stylistic/semi': ['error', 'always'], + '@stylistic/space-before-function-paren': [ + 'error', + { + anonymous: 'never', + asyncArrow: 'always', + named: 'never', + }, + ], + '@stylistic/spaced-comment': 'error', 'arrow-body-style': 'error', - 'arrow-parens': 'error', - 'arrow-spacing': 'error', - 'brace-style': 'error', - 'comma-dangle': ['error', 'always-multiline'], complexity: 'off', curly: ['error', 'multi-line'], 'dot-notation': 'error', - 'eol-last': 'error', eqeqeq: ['error', 'smart'], 'guard-for-in': 'error', - 'id-blacklist': [ + 'id-denylist': [ 'error', 'any', 'Number', @@ -221,43 +242,22 @@ module.exports = { 'import/order': ['error', { alphabetize: { order: 'asc' } }], 'jsdoc/check-alignment': 'error', 'jsdoc/check-indentation': 'error', - 'jsdoc/newline-after-description': 'error', + 'jsdoc/tag-lines': ['error', 'never', { startLines: 1 }], 'max-classes-per-file': 'error', - 'max-len': 'off', - 'new-parens': 'error', 'no-bitwise': 'error', 'no-caller': 'error', 'no-console': ['error', { allow: ['error', 'warn'] }], 'no-empty-function': 'error', 'no-eval': 'error', 'no-invalid-this': 'error', - 'no-multiple-empty-lines': 'error', 'no-new-wrappers': 'error', 'no-shadow': ['error', { hoist: 'all' }], 'no-throw-literal': 'error', - 'no-trailing-spaces': 'error', 'no-undef-init': 'error', 'no-unsafe-finally': 'error', - 'object-curly-spacing': ['error', 'always'], 'object-shorthand': 'error', 'one-var': ['error', 'never'], - 'quote-props': ['error', 'as-needed'], - quotes: [ - 'error', - 'single', - { avoidEscape: true }, - ], radix: 'error', - semi: ['error', 'always'], 'sort-keys': ['error', 'asc', { caseSensitive: false }], - 'space-before-function-paren': [ - 'error', - { - anonymous: 'never', - asyncArrow: 'always', - named: 'never', - }, - ], - 'spaced-comment': 'error', }, }; From 8892e6b24870636759413dfe4793e6e51aa95054 Mon Sep 17 00:00:00 2001 From: agatemosu Date: Thu, 5 Dec 2024 13:30:04 +0100 Subject: [PATCH 003/153] Fix --- .../js/artist-tracks-index/search-form.tsx | 3 +- .../js/beatmap-discussions/discussion-mode.ts | 2 +- resources/js/beatmap-discussions/editor.tsx | 2 +- .../js/beatmap-discussions/new-discussion.tsx | 2 +- .../js/beatmap-discussions/review-post.tsx | 2 +- resources/js/beatmapsets-show/hype.tsx | 2 +- resources/js/beatmapsets-show/info.tsx | 2 +- resources/js/components/comment.tsx | 4 +-- resources/js/components/sort.tsx | 2 +- resources/js/core/click-menu.ts | 2 +- resources/js/core/osu-audio/main.ts | 4 +-- resources/js/core/twitch-embed-player.d.ts | 2 +- .../js/entrypoints/notifications-index.tsx | 2 +- .../interfaces/beatmap-discussion-review.ts | 2 +- .../interfaces/beatmapset-discussion-json.ts | 12 +++---- .../js/interfaces/chat/channel-event-json.ts | 2 +- resources/js/interfaces/event-json.ts | 33 +++++++++---------- resources/js/interfaces/room-json.ts | 6 ++-- resources/js/interfaces/ruleset.ts | 2 +- .../js/models/chat/create-announcement.ts | 4 +-- resources/js/models/notification-stack.ts | 2 +- resources/js/models/notification-type.ts | 2 +- resources/js/news-index/post-item.tsx | 2 +- resources/js/news-show/main.tsx | 2 +- resources/js/notification-maps/message.ts | 2 +- .../js/notification-widget/item-group.tsx | 2 +- resources/js/notification-widget/item.tsx | 2 +- resources/js/notification-widget/main.tsx | 2 +- resources/js/notifications-index/main.tsx | 2 +- .../js/notifications/notification-resolver.ts | 2 +- .../profile-page/achievement-badge-popup.tsx | 4 +-- resources/js/profile-page/controller.tsx | 9 ++--- resources/js/profile-page/historical.tsx | 3 +- resources/js/profile-page/medals.tsx | 2 +- resources/js/profile-page/top-scores.tsx | 2 +- resources/js/profile-page/user-page.tsx | 4 +-- resources/js/store.ts | 2 +- resources/js/utils/ajax.ts | 2 +- .../js/utils/beatmapset-discussion-helper.ts | 6 ++-- resources/js/utils/url.ts | 2 +- .../notifications/notification-events.spec.ts | 2 +- 41 files changed, 70 insertions(+), 80 deletions(-) diff --git a/resources/js/artist-tracks-index/search-form.tsx b/resources/js/artist-tracks-index/search-form.tsx index 3fb7bed7e8f..0bd165adc3b 100644 --- a/resources/js/artist-tracks-index/search-form.tsx +++ b/resources/js/artist-tracks-index/search-form.tsx @@ -39,8 +39,7 @@ export type ArtistTrackSort = `${ArtistTrackSortField}_${ArtistTrackSortOrder}`; export const artistTrackSearchRelevanceParams = ['album', 'artist', 'query'] as const; type ArtistTrackSearchRelevanceParam = typeof artistTrackSearchRelevanceParams[number]; -const artistTrackSearchNumberRangeParams = ['bpm', 'length'] as const; -type ArtistTrackSearchNumberRangeParam = typeof artistTrackSearchNumberRangeParams[number]; +type ArtistTrackSearchNumberRangeParam = 'bpm' | 'length'; export type ArtistTrackSearch = { exclusive_only: boolean; diff --git a/resources/js/beatmap-discussions/discussion-mode.ts b/resources/js/beatmap-discussions/discussion-mode.ts index bae742fe778..79c84d06e6d 100644 --- a/resources/js/beatmap-discussions/discussion-mode.ts +++ b/resources/js/beatmap-discussions/discussion-mode.ts @@ -4,6 +4,6 @@ import DiscussionPage from './discussion-page'; type DiscussionMode = Exclude; -export const discussionModes: Readonly = ['reviews', 'generalAll', 'general', 'timeline'] as const; +export const discussionModes: readonly DiscussionMode[] = ['reviews', 'generalAll', 'general', 'timeline'] as const; export default DiscussionMode; diff --git a/resources/js/beatmap-discussions/editor.tsx b/resources/js/beatmap-discussions/editor.tsx index 10f0845511f..b3517faf381 100644 --- a/resources/js/beatmap-discussions/editor.tsx +++ b/resources/js/beatmap-discussions/editor.tsx @@ -124,7 +124,7 @@ export default class Editor extends React.Component { if (saved != null) { try { this.initialValue = JSON.parse(saved) as SlateElement[]; - } catch (error) { + } catch (_error) { console.error('invalid json in localStorage, ignoring'); localStorage.removeItem(this.localStorageKey); } diff --git a/resources/js/beatmap-discussions/new-discussion.tsx b/resources/js/beatmap-discussions/new-discussion.tsx index 5fc702e49e8..db31e81352a 100644 --- a/resources/js/beatmap-discussions/new-discussion.tsx +++ b/resources/js/beatmap-discussions/new-discussion.tsx @@ -501,7 +501,7 @@ export class NewDiscussion extends React.Component { private readonly updateStickToHeight = () => this.stickToHeight = this.props.stickTo?.current?.getBoundingClientRect().height; private validPost(type: string): type is DiscussionType { - if (!(discussionTypes as Readonly).includes(type)) return false; + if (!(discussionTypes as readonly string[]).includes(type)) return false; if (!validMessageLength(this.message, this.isTimeline)) return false; if (!this.isTimeline) return true; diff --git a/resources/js/beatmap-discussions/review-post.tsx b/resources/js/beatmap-discussions/review-post.tsx index caf6e9cceba..be2db027a4d 100644 --- a/resources/js/beatmap-discussions/review-post.tsx +++ b/resources/js/beatmap-discussions/review-post.tsx @@ -34,7 +34,7 @@ export class ReviewPost extends React.Component { break; } }); - } catch (e) { + } catch (_error) { docBlocks.push(
[error parsing review]
); } diff --git a/resources/js/beatmapsets-show/hype.tsx b/resources/js/beatmapsets-show/hype.tsx index f98d6eafc5c..6b007a93a64 100644 --- a/resources/js/beatmapsets-show/hype.tsx +++ b/resources/js/beatmapsets-show/hype.tsx @@ -81,10 +81,10 @@ export default class Hype extends React.Component {

) : (

)} diff --git a/resources/js/beatmapsets-show/info.tsx b/resources/js/beatmapsets-show/info.tsx index a90cdb9e042..a6c3ca251f0 100644 --- a/resources/js/beatmapsets-show/info.tsx +++ b/resources/js/beatmapsets-show/info.tsx @@ -121,10 +121,10 @@ export default class Info extends React.Component {

diff --git a/resources/js/components/comment.tsx b/resources/js/components/comment.tsx index 0f5985fc931..65413c6c0e0 100644 --- a/resources/js/components/comment.tsx +++ b/resources/js/components/comment.tsx @@ -130,7 +130,7 @@ export default class Comment extends React.Component { const replies = this.replies; - return replies.length > 0 && replies.some((reply) => reply.isVisible); + return replies.some((reply) => reply.isVisible); } @computed @@ -510,10 +510,10 @@ export default class Comment extends React.Component { : this.shouldRenderContent && <>
{this.isLongContent && this.renderToggleClipButton()} diff --git a/resources/js/components/sort.tsx b/resources/js/components/sort.tsx index fe55db69aa1..9602ff95d76 100644 --- a/resources/js/components/sort.tsx +++ b/resources/js/components/sort.tsx @@ -13,7 +13,7 @@ interface Props { showTitle?: boolean; title?: string; transPrefix: string; - values: Readonly; + values: readonly string[]; } export class Sort extends React.PureComponent { diff --git a/resources/js/core/click-menu.ts b/resources/js/core/click-menu.ts index 753f78df4ed..f760b1c5c95 100644 --- a/resources/js/core/click-menu.ts +++ b/resources/js/core/click-menu.ts @@ -47,7 +47,7 @@ export default class ClickMenu { } }; - show = (target?: string | null | undefined) => { + show = (target?: string | null) => { const previousTree = this.tree(); this.current = target; diff --git a/resources/js/core/osu-audio/main.ts b/resources/js/core/osu-audio/main.ts index f0b5bcc9ffd..6480252ce74 100644 --- a/resources/js/core/osu-audio/main.ts +++ b/resources/js/core/osu-audio/main.ts @@ -170,7 +170,7 @@ export default class Main { this.setState('loading'); const promise = this.audio.play(); // old api returns undefined - promise?.catch((error: { name: string }) => { + promise?.catch((error: DOMException) => { if (Main.ignoredErrors.includes(error.name)) { console.error('playback failed:', error.name); this.stop(); @@ -213,7 +213,7 @@ export default class Main { if (node.classList.contains('js-audio--player')) { newPlayers.push(node); } else { - for (const player of [...node.querySelectorAll('.js-audio--player')]) { + for (const player of node.querySelectorAll('.js-audio--player')) { if (player instanceof HTMLElement) { newPlayers.push(player); } diff --git a/resources/js/core/twitch-embed-player.d.ts b/resources/js/core/twitch-embed-player.d.ts index 58a3e4a632e..7f4fd471ff3 100644 --- a/resources/js/core/twitch-embed-player.d.ts +++ b/resources/js/core/twitch-embed-player.d.ts @@ -6,7 +6,7 @@ declare module 'twitch-embed-player' { export default class TwitchEmbedPlayer { static PLAY: string; - constructor (id: string, options: Record); + constructor(id: string, options: Record); addEventListener(action: string, callback: () => void): void; } } diff --git a/resources/js/entrypoints/notifications-index.tsx b/resources/js/entrypoints/notifications-index.tsx index f3c8846844a..db90cf00352 100644 --- a/resources/js/entrypoints/notifications-index.tsx +++ b/resources/js/entrypoints/notifications-index.tsx @@ -2,8 +2,8 @@ // See the LICENCE file in the repository root for full licence text. import { dispatch } from 'app-dispatcher'; -import { Main } from 'notifications-index/main'; import { NotificationEventMoreLoaded } from 'notifications/notification-events'; +import { Main } from 'notifications-index/main'; import core from 'osu-core-singleton'; import * as React from 'react'; import { parseJson } from 'utils/json'; diff --git a/resources/js/interfaces/beatmap-discussion-review.ts b/resources/js/interfaces/beatmap-discussion-review.ts index 92e5c87fd37..4c41dd27c76 100644 --- a/resources/js/interfaces/beatmap-discussion-review.ts +++ b/resources/js/interfaces/beatmap-discussion-review.ts @@ -32,5 +32,5 @@ export type PersistedBeatmapReviewBlock = DocumentParagraph | PersistedDocumentI export type PersistedBeatmapDiscussionReview = PersistedBeatmapReviewBlock[]; export function isBeatmapReviewDiscussionType(type: string): type is BeatmapReviewDiscussionType { - return (beatmapReviewDiscussionTypes as Readonly).includes(type); + return (beatmapReviewDiscussionTypes as readonly string[]).includes(type); } diff --git a/resources/js/interfaces/beatmapset-discussion-json.ts b/resources/js/interfaces/beatmapset-discussion-json.ts index 5494d6a66da..4c2b76facdf 100644 --- a/resources/js/interfaces/beatmapset-discussion-json.ts +++ b/resources/js/interfaces/beatmapset-discussion-json.ts @@ -52,14 +52,10 @@ export default BeatmapsetDiscussionJson; // bundle versions; beatmap is only on modding history events version export type BeatmapsetDiscussionJsonForBundle = -Omit // bundle explicitly does not include posts; need this for type discrimination. -& Required>; + Omit // bundle explicitly does not include posts; need this for type discrimination. +& Required>; // discussions page version export type BeatmapsetDiscussionJsonForShow = -BeatmapsetDiscussionJson & Required>; + BeatmapsetDiscussionJson +& Required>; diff --git a/resources/js/interfaces/chat/channel-event-json.ts b/resources/js/interfaces/chat/channel-event-json.ts index 877889ff2b3..7a4e948d02b 100644 --- a/resources/js/interfaces/chat/channel-event-json.ts +++ b/resources/js/interfaces/chat/channel-event-json.ts @@ -8,7 +8,7 @@ const channelEvents = ['chat.channel.join', 'chat.channel.part'] as const; type ChannelEvent = (typeof channelEvents)[number]; export function isChannelEvent(arg: SocketEventData): arg is ChannelEventJson { - return arg.event != null && (channelEvents as Readonly).includes(arg.event); + return arg.event != null && (channelEvents as readonly string[]).includes(arg.event); } export default interface ChannelEventJson { diff --git a/resources/js/interfaces/event-json.ts b/resources/js/interfaces/event-json.ts index 8afc0298f2b..8727ad528f2 100644 --- a/resources/js/interfaces/event-json.ts +++ b/resources/js/interfaces/event-json.ts @@ -5,23 +5,20 @@ import AchievementJson from './achievement-json'; import Rank from './rank'; import Ruleset from './ruleset'; -const eventTypes = [ - 'achievement', - 'beatmapPlaycount', - 'beatmapsetApprove', - 'beatmapsetDelete', - 'beatmapsetRevive', - 'beatmapsetUpdate', - 'beatmapsetUpload', - 'rank', - 'rankLost', - 'usernameChange', - 'userSupportAgain', - 'userSupportFirst', - 'userSupportGift', -] as const; - -type EventType = (typeof eventTypes)[number]; +type EventType = + | 'achievement' + | 'beatmapPlaycount' + | 'beatmapsetApprove' + | 'beatmapsetDelete' + | 'beatmapsetRevive' + | 'beatmapsetUpdate' + | 'beatmapsetUpload' + | 'rank' + | 'rankLost' + | 'usernameChange' + | 'userSupportAgain' + | 'userSupportFirst' + | 'userSupportGift'; interface EventBase { created_at: string; @@ -126,7 +123,7 @@ interface UserSupportGiftEvent extends EventBase { } type EventJson = - AchievementEvent + | AchievementEvent | BeatmapPlaycountEvent | BeatmapsetApproveEvent | BeatmapsetDeleteEvent diff --git a/resources/js/interfaces/room-json.ts b/resources/js/interfaces/room-json.ts index 25df9ea6088..a2e6d55e4c9 100644 --- a/resources/js/interfaces/room-json.ts +++ b/resources/js/interfaces/room-json.ts @@ -4,11 +4,9 @@ import UserJson from 'interfaces/user-json'; import PlaylistItemJson from './playlist-item-json'; -const roomCategories = ['normal', 'spotlight'] as const; -export type RoomCategory = (typeof roomCategories)[number]; +export type RoomCategory = 'normal' | 'spotlight'; -const roomTypes = ['playlists', 'head_to_head', 'team_versus'] as const; -export type RoomType = (typeof roomTypes)[number]; +export type RoomType = 'playlists' | 'head_to_head' | 'team_versus'; interface RoomJsonAvailableIncludes { current_playlist_item: PlaylistItemJson | null; diff --git a/resources/js/interfaces/ruleset.ts b/resources/js/interfaces/ruleset.ts index 23cf8e0c5c3..39a1034dd97 100644 --- a/resources/js/interfaces/ruleset.ts +++ b/resources/js/interfaces/ruleset.ts @@ -4,7 +4,7 @@ export const rulesets = ['osu', 'taiko', 'fruits', 'mania'] as const; export function ensureRuleset(maybeRuleset: string): Ruleset | undefined { - if ((rulesets as Readonly).includes(maybeRuleset)) { + if ((rulesets as readonly string[]).includes(maybeRuleset)) { return maybeRuleset as Ruleset; } } diff --git a/resources/js/models/chat/create-announcement.ts b/resources/js/models/chat/create-announcement.ts index 32000b412f4..2e6e34e05af 100644 --- a/resources/js/models/chat/create-announcement.ts +++ b/resources/js/models/chat/create-announcement.ts @@ -24,7 +24,7 @@ export const maxLengths = Object.freeze({ }); export function isInputKey(key: string): key is InputKey { - return (inputKeys as Readonly).includes(key); + return (inputKeys as readonly string[]).includes(key); } // This class is owned by ChatStateStore @@ -88,7 +88,7 @@ export default class CreateAnnouncement { this.inputs.message = json.message; this.inputs.name = json.name; this.inputs.users = [...json.validUsers, json.users].join(','); - } catch (error) { + } catch (_error) { console.error('invalid json in localStorage'); localStorage.removeItem(localStorageKey); } diff --git a/resources/js/models/notification-stack.ts b/resources/js/models/notification-stack.ts index e504b74ddbf..0b907940b46 100644 --- a/resources/js/models/notification-stack.ts +++ b/resources/js/models/notification-stack.ts @@ -5,12 +5,12 @@ import { NotificationStackJson } from 'interfaces/notification-json'; import { action, computed, makeObservable, observable } from 'mobx'; import Notification from 'models/notification'; import { Name } from 'models/notification-type'; -import { NotificationContextData } from 'notifications-context'; import { NotificationCursor } from 'notifications/notification-cursor'; import NotificationDeletable from 'notifications/notification-deletable'; import { NotificationIdentity } from 'notifications/notification-identity'; import NotificationReadable from 'notifications/notification-readable'; import { NotificationResolver } from 'notifications/notification-resolver'; +import { NotificationContextData } from 'notifications-context'; export default class NotificationStack implements NotificationReadable, NotificationDeletable { @observable cursor: NotificationCursor | null = null; diff --git a/resources/js/models/notification-type.ts b/resources/js/models/notification-type.ts index 9444775aa20..bf173b75618 100644 --- a/resources/js/models/notification-type.ts +++ b/resources/js/models/notification-type.ts @@ -4,12 +4,12 @@ import { NotificationTypeJson } from 'interfaces/notification-json'; import { action, computed, makeObservable, observable } from 'mobx'; import NotificationStack from 'models/notification-stack'; -import { NotificationContextData } from 'notifications-context'; import { NotificationCursor } from 'notifications/notification-cursor'; import NotificationDeletable from 'notifications/notification-deletable'; import { NotificationIdentity } from 'notifications/notification-identity'; import NotificationReadable from 'notifications/notification-readable'; import { NotificationResolver } from 'notifications/notification-resolver'; +import { NotificationContextData } from 'notifications-context'; // List is in the order they appear on the notification filter. export const typeNames = [null, 'user', 'beatmapset', 'forum_topic', 'news_post', 'build', 'channel'] as const; diff --git a/resources/js/news-index/post-item.tsx b/resources/js/news-index/post-item.tsx index e2fd7176343..e8e34963475 100644 --- a/resources/js/news-index/post-item.tsx +++ b/resources/js/news-index/post-item.tsx @@ -41,8 +41,8 @@ export default function PostItem({ modifiers, post }: { modifiers?: string[]; po
{post.title}
{

-
+
{this.renderNav()} diff --git a/resources/js/notification-maps/message.ts b/resources/js/notification-maps/message.ts index e3ead587a18..5be165ede97 100644 --- a/resources/js/notification-maps/message.ts +++ b/resources/js/notification-maps/message.ts @@ -2,8 +2,8 @@ // See the LICENCE file in the repository root for full licence text. import Notification from 'models/notification'; -import NotificationDetails from 'models/notification-details'; import { isBeatmapOwnerChangeNotification } from 'models/notification/beatmap-owner-change-notification'; +import NotificationDetails from 'models/notification-details'; import { trans, transArray, transChoice, transExists } from 'utils/lang'; type Replacements = { title: string } & Partial>; diff --git a/resources/js/notification-widget/item-group.tsx b/resources/js/notification-widget/item-group.tsx index 741b70f0aad..981f34e2763 100644 --- a/resources/js/notification-widget/item-group.tsx +++ b/resources/js/notification-widget/item-group.tsx @@ -8,9 +8,9 @@ import NotificationStack from 'models/notification-stack'; import { categoryToIcons } from 'notification-maps/icons'; import { formatMessageGroup } from 'notification-maps/message'; import { urlGroup } from 'notification-maps/url'; -import { NotificationContext } from 'notifications-context'; import NotificationDeleteButton from 'notifications/notification-delete-button'; import NotificationReadButton from 'notifications/notification-read-button'; +import { NotificationContext } from 'notifications-context'; import * as React from 'react'; import { trans, transChoice } from 'utils/lang'; import Item from './item'; diff --git a/resources/js/notification-widget/item.tsx b/resources/js/notification-widget/item.tsx index ada41ed5a85..658d3e6622a 100644 --- a/resources/js/notification-widget/item.tsx +++ b/resources/js/notification-widget/item.tsx @@ -4,9 +4,9 @@ import TimeWithTooltip from 'components/time-with-tooltip'; import { observer } from 'mobx-react'; import Notification from 'models/notification'; -import { NotificationContext } from 'notifications-context'; import NotificationDeleteButton from 'notifications/notification-delete-button'; import NotificationReadButton from 'notifications/notification-read-button'; +import { NotificationContext } from 'notifications-context'; import * as React from 'react'; import { classWithModifiers, mergeModifiers, urlPresence } from 'utils/css'; import { trans } from 'utils/lang'; diff --git a/resources/js/notification-widget/main.tsx b/resources/js/notification-widget/main.tsx index 667c7f7380f..a0b9d99beeb 100644 --- a/resources/js/notification-widget/main.tsx +++ b/resources/js/notification-widget/main.tsx @@ -6,9 +6,9 @@ import { route } from 'laroute'; import { computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import { Name, typeNames } from 'models/notification-type'; -import { NotificationContext } from 'notifications-context'; import NotificationController from 'notifications/notification-controller'; import NotificationReadButton from 'notifications/notification-read-button'; +import { NotificationContext } from 'notifications-context'; import core from 'osu-core-singleton'; import * as React from 'react'; import { classWithModifiers } from 'utils/css'; diff --git a/resources/js/notifications-index/main.tsx b/resources/js/notifications-index/main.tsx index 904dd325bd9..e332c06d487 100644 --- a/resources/js/notifications-index/main.tsx +++ b/resources/js/notifications-index/main.tsx @@ -9,10 +9,10 @@ import { computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import { Name as NotificationTypeName, typeNames } from 'models/notification-type'; import Stack from 'notification-widget/stack'; -import { NotificationContext, NotificationContextData } from 'notifications-context'; import NotificationController from 'notifications/notification-controller'; import NotificationDeleteButton from 'notifications/notification-delete-button'; import NotificationReadButton from 'notifications/notification-read-button'; +import { NotificationContext, NotificationContextData } from 'notifications-context'; import core from 'osu-core-singleton'; import * as React from 'react'; import { trans } from 'utils/lang'; diff --git a/resources/js/notifications/notification-resolver.ts b/resources/js/notifications/notification-resolver.ts index 1f4b1f522b9..010a629b244 100644 --- a/resources/js/notifications/notification-resolver.ts +++ b/resources/js/notifications/notification-resolver.ts @@ -7,10 +7,10 @@ import { route } from 'laroute'; import { debounce } from 'lodash'; import { action, makeObservable } from 'mobx'; import Notification from 'models/notification'; -import { NotificationContextData } from 'notifications-context'; import NotificationDeletable from 'notifications/notification-deletable'; import { NotificationIdentity, resolveIdentityType, toJson, toString } from 'notifications/notification-identity'; import NotificationReadable from 'notifications/notification-readable'; +import { NotificationContextData } from 'notifications-context'; import { onError } from 'utils/ajax'; import { NotificationCursor } from './notification-cursor'; import { NotificationEventDelete, NotificationEventMoreLoaded, NotificationEventRead } from './notification-events'; diff --git a/resources/js/profile-page/achievement-badge-popup.tsx b/resources/js/profile-page/achievement-badge-popup.tsx index dcddeafbd62..5a93ba6afcc 100644 --- a/resources/js/profile-page/achievement-badge-popup.tsx +++ b/resources/js/profile-page/achievement-badge-popup.tsx @@ -37,15 +37,15 @@ export default function AchievementBadgePopup({ achievedAt, achievement }: Props {achievement.name}
{achievement.instructions != null && (
)} diff --git a/resources/js/profile-page/controller.tsx b/resources/js/profile-page/controller.tsx index ec5fbc44af7..93949127fa3 100644 --- a/resources/js/profile-page/controller.tsx +++ b/resources/js/profile-page/controller.tsx @@ -43,8 +43,8 @@ const sectionToUrlType = { } as const; // #region lazy loaded extra pages -const beatmapsetsExtraPageKeys = ['favourite', 'graveyard', 'guest', 'loved', 'nominated', 'pending', 'ranked'] as const; -type BeatmapsetsJson = Record>; +type BeatmapsetsExtraPageKeys = 'favourite' | 'graveyard' | 'guest' | 'loved' | 'nominated' | 'pending' | 'ranked'; +type BeatmapsetsJson = Record>; interface HistoricalJson { beatmap_playcounts: PageSectionJson; @@ -52,8 +52,9 @@ interface HistoricalJson { recent: PageSectionJson; replays_watched_counts: UserReplaysWatchedCountJson[]; } -const topScoresKeys = ['best', 'firsts', 'pinned'] as const; -type TopScoresJson = Record>; + +type TopScoresKeys = 'best' | 'firsts' | 'pinned'; +type TopScoresJson = Record>; // #endregion export function validPage(page: unknown) { diff --git a/resources/js/profile-page/historical.tsx b/resources/js/profile-page/historical.tsx index a74afed377c..e39994ba844 100644 --- a/resources/js/profile-page/historical.tsx +++ b/resources/js/profile-page/historical.tsx @@ -16,8 +16,7 @@ import ExtraHeader from './extra-header'; import ExtraPageProps, { HistoricalSection } from './extra-page-props'; import PlayDetailList from './play-detail-list'; -const chartSections = ['monthly_playcounts', 'replays_watched_counts'] as const; -type ChartSection = typeof chartSections[number]; +type ChartSection = 'monthly_playcounts' | 'replays_watched_counts'; // conveniently both charts share same interface interface RawChartData { diff --git a/resources/js/profile-page/medals.tsx b/resources/js/profile-page/medals.tsx index 2d892eba64d..0bcb09c1ee5 100644 --- a/resources/js/profile-page/medals.tsx +++ b/resources/js/profile-page/medals.tsx @@ -28,7 +28,7 @@ export default class Medals extends React.Component { // group by .grouping and then further group by .ordering const ret = new Map>(); - for (const achievement of [...this.props.controller.achievements.values()]) { + for (const achievement of this.props.controller.achievements.values()) { const userAchievement = this.userAchievements[achievement.id.toString()]; const visible = this.currentModeFilter(achievement) && (isCurrentUser || userAchievement != null); diff --git a/resources/js/profile-page/top-scores.tsx b/resources/js/profile-page/top-scores.tsx index bbbe05e8fd4..20f190d874a 100644 --- a/resources/js/profile-page/top-scores.tsx +++ b/resources/js/profile-page/top-scores.tsx @@ -26,8 +26,8 @@ export default class TopScores extends React.Component { {' '}
)} diff --git a/resources/js/profile-page/user-page.tsx b/resources/js/profile-page/user-page.tsx index c6c13dc6957..c249015bf10 100644 --- a/resources/js/profile-page/user-page.tsx +++ b/resources/js/profile-page/user-page.tsx @@ -88,8 +88,8 @@ export default class UserPage extends React.Component {

{!this.props.controller.state.user.has_supported && ( @@ -117,8 +117,8 @@ export default class UserPage extends React.Component { private renderPageShow() { return (

); } diff --git a/resources/js/store.ts b/resources/js/store.ts index 06a4819e533..bc649e9c352 100644 --- a/resources/js/store.ts +++ b/resources/js/store.ts @@ -70,7 +70,7 @@ export class Store { customAttributes: [{ key: 'orderId', value: orderId }], lineItems: this.collectShopifyItems(), }); - } catch (error) { + } catch (_error) { hideLoadingOverlay(); popup(trans('errors.checkout.generic'), 'danger'); return; diff --git a/resources/js/utils/ajax.ts b/resources/js/utils/ajax.ts index cf23d147c7a..125b3f98818 100644 --- a/resources/js/utils/ajax.ts +++ b/resources/js/utils/ajax.ts @@ -54,7 +54,7 @@ export function xhrErrorMessage(xhr?: JQuery.jqXHR) { const json = xhr.responseJSON as UnknownErrorJson; if (json.validation_error != null) { - return `${Object.values(json.validation_error).flat().join(', ')}.`; + return `${json.validation_error.flatMap((err) => Object.values(err)).join(', ')}.`; } let message = json.error ?? json.message ?? ''; diff --git a/resources/js/utils/beatmapset-discussion-helper.ts b/resources/js/utils/beatmapset-discussion-helper.ts index 476d28918c5..767d8dae8ff 100644 --- a/resources/js/utils/beatmapset-discussion-helper.ts +++ b/resources/js/utils/beatmapset-discussion-helper.ts @@ -146,12 +146,12 @@ function isNearbyDiscussion(discussion: T): } // sync with $defaultRulesets in app/Models/UserGroup.php -const defaultGroupRulesets: Partial>> = { nat: rulesets }; +const defaultGroupRulesets: Partial> = { nat: rulesets }; export function isUserFullNominator(user?: UserJson | null, gameMode?: Ruleset) { return user != null && user.groups != null && user.groups.some((group) => { if (gameMode != null) { - let groupRulesets: Readonly = group.playmodes ?? []; + let groupRulesets: readonly Ruleset[] = group.playmodes ?? []; if (groupRulesets.length === 0) { groupRulesets = defaultGroupRulesets[group.identifier] ?? []; } @@ -350,7 +350,7 @@ export function propsFromHref(href = '') { // Either accept that as fact of life or a better regexp is needed which is // probably rather difficult especially if we're going to support parsing IDN. targetUrl = new URL(href); - } catch (e: unknown) { + } catch (_error) { // ignore error } diff --git a/resources/js/utils/url.ts b/resources/js/utils/url.ts index c5ac2f3d16f..3288029617c 100644 --- a/resources/js/utils/url.ts +++ b/resources/js/utils/url.ts @@ -111,7 +111,7 @@ export function updateQueryString(url: string | null, params: Record { const stack = store.unreadStacks.getStack(extra); expect(stack).toBeDefined(); if (stack != null) { - expect([...stack?.notifications.values()].find((notification) => notification.isRead)).toBeUndefined(); + expect([...stack.notifications.values()].find((notification) => notification.isRead)).toBeUndefined(); } }); }); From e839cd92e55ff1a882a4aba983d0c0553dfd7e21 Mon Sep 17 00:00:00 2001 From: agatemosu Date: Thu, 5 Dec 2024 13:30:10 +0100 Subject: [PATCH 004/153] Update --max-warnings --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 094e6063232..1b976bbfb89 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -49,7 +49,7 @@ jobs: - name: Install js dependencies run: yarn --frozen-lockfile - - run: 'yarn lint --max-warnings 87 > /dev/null' + - run: 'yarn lint --max-warnings 86 > /dev/null' - run: yarn pretty From bf3a6ed290a0837777c62ef9fb1f96e3638211ac Mon Sep 17 00:00:00 2001 From: Agatem Date: Thu, 5 Dec 2024 22:14:59 +0100 Subject: [PATCH 005/153] Remove forgotten deprecated rules --- .eslintrc.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index a82f827ccf5..757bd63f666 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -120,8 +120,6 @@ module.exports = { 'dot-notation': 'off', 'no-invalid-this': 'off', 'no-shadow': 'off', - 'object-curly-spacing': 'off', - quotes: 'off', 'react-hooks/exhaustive-deps': 'error', 'react/jsx-boolean-value': 'error', 'react/jsx-curly-spacing': 'error', From 5bacdc6a95676eb20300ce65ebb61caa08377a1e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 10 Dec 2024 15:36:51 +0900 Subject: [PATCH 006/153] Add `beatmapset_id` to multiplayer playlist items --- app/Models/Multiplayer/PlaylistItem.php | 2 ++ .../Multiplayer/PlaylistItemTransformer.php | 1 + ...apset_id_to_multiplayer_playlist_items.php | 29 +++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 database/migrations/2024_12_10_035240_add_beatmapset_id_to_multiplayer_playlist_items.php diff --git a/app/Models/Multiplayer/PlaylistItem.php b/app/Models/Multiplayer/PlaylistItem.php index 4c8e6175505..73d70ae77ba 100644 --- a/app/Models/Multiplayer/PlaylistItem.php +++ b/app/Models/Multiplayer/PlaylistItem.php @@ -16,6 +16,7 @@ * @property json|null $allowed_mods * @property Beatmap $beatmap * @property int $beatmap_id + * @property int|null $beatmapset_id * @property \Carbon\Carbon|null $created_at * @property int $id * @property int $owner_id @@ -64,6 +65,7 @@ public static function fromJsonParams(User $owner, $json) $obj->$field = $value; } + $obj->beatmapset_id = get_int($json['beatmapset_id'] ?? null); $obj->max_attempts = get_int($json['max_attempts'] ?? null); $modsHelper = app('mods'); diff --git a/app/Transformers/Multiplayer/PlaylistItemTransformer.php b/app/Transformers/Multiplayer/PlaylistItemTransformer.php index 99cd7f98d83..7caf2ab77ab 100644 --- a/app/Transformers/Multiplayer/PlaylistItemTransformer.php +++ b/app/Transformers/Multiplayer/PlaylistItemTransformer.php @@ -21,6 +21,7 @@ public function transform(PlaylistItem $item) 'id' => $item->id, 'room_id' => $item->room_id, 'beatmap_id' => $item->beatmap_id, + 'beatmapset_id' => $item->beatmapset_id, 'ruleset_id' => $item->ruleset_id, 'allowed_mods' => $item->allowed_mods, 'required_mods' => $item->required_mods, diff --git a/database/migrations/2024_12_10_035240_add_beatmapset_id_to_multiplayer_playlist_items.php b/database/migrations/2024_12_10_035240_add_beatmapset_id_to_multiplayer_playlist_items.php new file mode 100644 index 00000000000..70106407a55 --- /dev/null +++ b/database/migrations/2024_12_10_035240_add_beatmapset_id_to_multiplayer_playlist_items.php @@ -0,0 +1,29 @@ +unsignedMediumInteger('beatmapset_id')->nullable()->after('beatmap_id'); + }); + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('multiplayer_playlist_items', function (Blueprint $table) { + $table->dropColumn('beatmapset_id'); + }); + } +}; From 545c945125919800fc4866d992b6d2d6fc954b29 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 10 Dec 2024 21:34:51 +0900 Subject: [PATCH 007/153] render user tags --- .../Controllers/BeatmapsetsController.php | 1 + .../BeatmapsetCompactTransformer.php | 8 ++++++++ resources/js/beatmapsets-show/info.tsx | 19 ++++++++++++------- .../js/interfaces/beatmapset-extended-json.ts | 1 + resources/js/interfaces/beatmapset-json.ts | 2 ++ resources/js/interfaces/tag-json.ts | 8 ++++++++ 6 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 resources/js/interfaces/tag-json.ts diff --git a/app/Http/Controllers/BeatmapsetsController.php b/app/Http/Controllers/BeatmapsetsController.php index d0958f9567a..d8f91d7b5ad 100644 --- a/app/Http/Controllers/BeatmapsetsController.php +++ b/app/Http/Controllers/BeatmapsetsController.php @@ -417,6 +417,7 @@ private function showJson($beatmapset) 'recent_favourites', 'related_users', 'user', + 'user_tags', ]); } } diff --git a/app/Transformers/BeatmapsetCompactTransformer.php b/app/Transformers/BeatmapsetCompactTransformer.php index 546fe4125fe..fd3f7d0694c 100644 --- a/app/Transformers/BeatmapsetCompactTransformer.php +++ b/app/Transformers/BeatmapsetCompactTransformer.php @@ -41,6 +41,7 @@ class BeatmapsetCompactTransformer extends TransformerAbstract 'recent_favourites', 'related_users', 'user', + 'user_tags', ]; // TODO: switch to enum after php 8.1 @@ -299,6 +300,13 @@ public function includeRelatedUsers(Beatmapset $beatmapset) return $this->collection($users, new UserCompactTransformer()); } + public function includeUserTags(Beatmapset $beatmapset) + { + $beatmaps = $this->beatmaps($beatmapset); + + return $this->collection($beatmaps->flatMap->topTags(), new TagTransformer()); + } + private function beatmaps(Beatmapset $beatmapset, ?Fractal\ParamBag $params = null): EloquentCollection { $rel = $beatmapset->trashed() || ($params !== null && $params->get('with_trashed')) ? 'allBeatmaps' : 'beatmaps'; diff --git a/resources/js/beatmapsets-show/info.tsx b/resources/js/beatmapsets-show/info.tsx index a90cdb9e042..e1974cbda50 100644 --- a/resources/js/beatmapsets-show/info.tsx +++ b/resources/js/beatmapsets-show/info.tsx @@ -65,6 +65,15 @@ export default class Info extends React.Component { return ret; } + private get tags() { + return [ + ...this.controller.beatmapset.user_tags.map((tag) => tag.name), + ...this.controller.beatmapset.tags + .split(' ') + .filter(present), + ]; + } + private get withEditDescription() { return this.controller.beatmapset.description.bbcode != null; } @@ -84,10 +93,6 @@ export default class Info extends React.Component { } render() { - const tags = this.controller.beatmapset.tags - .split(' ') - .filter(present); - return (
{this.isEditingDescription && @@ -191,14 +196,14 @@ export default class Info extends React.Component {
- {tags.length > 0 && + {this.tags.length > 0 &&

{trans('beatmapsets.show.info.tags')}

- {tags.map((tag, i) => ( - + {this.tags.map((tag) => ( + >; export type BeatmapsetJsonForShow = diff --git a/resources/js/interfaces/beatmapset-json.ts b/resources/js/interfaces/beatmapset-json.ts index 533ee955779..cb59fc13390 100644 --- a/resources/js/interfaces/beatmapset-json.ts +++ b/resources/js/interfaces/beatmapset-json.ts @@ -9,6 +9,7 @@ import BeatmapsetNominationJson from './beatmapset-nomination-json'; import GenreJson from './genre-json'; import LanguageJson from './language-json'; import Ruleset from './ruleset'; +import TagJson from './tag-json'; import UserJson, { UserJsonDeleted } from './user-json'; export interface Availability { @@ -94,6 +95,7 @@ interface BeatmapsetJsonAvailableIncludes { recent_favourites: UserJson[]; related_users: UserJson[]; user: UserJson | UserJsonDeleted; + user_tags: TagJson[]; } interface HypeData { diff --git a/resources/js/interfaces/tag-json.ts b/resources/js/interfaces/tag-json.ts new file mode 100644 index 00000000000..5d1352a7a4b --- /dev/null +++ b/resources/js/interfaces/tag-json.ts @@ -0,0 +1,8 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +export default interface TagJson { + description: string; + id: number; + name: string; +} From 4ae47f9f743c928a7f6412f395f7eaba35056b12 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Thu, 12 Dec 2024 15:57:19 +0900 Subject: [PATCH 008/153] cache tags locally, only cache beatmap tags with ids and counts; return description with response? --- .../Controllers/BeatmapTagsController.php | 9 +---- app/Http/Controllers/TagsController.php | 9 +---- app/Models/Beatmap.php | 26 ++++++++++++++ app/Models/Tag.php | 6 ++-- app/Providers/AppServiceProvider.php | 1 + app/Singletons/Tags.php | 35 +++++++++++++++++++ 6 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 app/Singletons/Tags.php diff --git a/app/Http/Controllers/BeatmapTagsController.php b/app/Http/Controllers/BeatmapTagsController.php index 87c9d9b253b..630010f7564 100644 --- a/app/Http/Controllers/BeatmapTagsController.php +++ b/app/Http/Controllers/BeatmapTagsController.php @@ -29,15 +29,8 @@ public function __construct() public function index($beatmapId) { - $topBeatmapTags = cache_remember_mutexed( - "beatmap_tags:{$beatmapId}", - $GLOBALS['cfg']['osu']['tags']['beatmap_tags_cache_duration'], - [], - fn () => Tag::topTags($beatmapId), - ); - return [ - 'beatmap_tags' => $topBeatmapTags, + 'beatmap_tags' => Beatmap::findOrFail($beatmapId)->topTagsJson(), ]; } diff --git a/app/Http/Controllers/TagsController.php b/app/Http/Controllers/TagsController.php index 80b657d9182..a93ddeff028 100644 --- a/app/Http/Controllers/TagsController.php +++ b/app/Http/Controllers/TagsController.php @@ -21,15 +21,8 @@ public function __construct() public function index() { - $tags = cache_remember_mutexed( - 'tags', - $GLOBALS['cfg']['osu']['tags']['tags_cache_duration'], - [], - fn () => Tag::all(), - ); - return [ - 'tags' => json_collection($tags, new TagTransformer()), + 'tags' => app('tags')->json(), ]; } } diff --git a/app/Models/Beatmap.php b/app/Models/Beatmap.php index b107d638d17..a042eebcf9c 100644 --- a/app/Models/Beatmap.php +++ b/app/Models/Beatmap.php @@ -348,6 +348,32 @@ public function status() return array_search($this->approved, Beatmapset::STATES, true); } + public function topTagsJson() + { + $tagIds = cache_remember_mutexed( + "beatmap_top_tag_ids:{$this->getKey()}", + $GLOBALS['cfg']['osu']['tags']['beatmap_tags_cache_duration'], + [], + fn () => Tag::topTagIds($this->getKey())->toArray(), + ); + + $cachedTags = app('tags'); + $json = []; + + foreach ($tagIds as $tagId) { + $tag = $cachedTags->get($tagId['id']); + if ($tag !== null) { + $json[] = [ + ...$tagId, + 'description' => $tag->description, + 'name' => $tag->name, + ]; + } + } + + return $json; + } + private function getDifficultyrating() { if ($this->convert) { diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 3513ab3d891..74293c5c00c 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -23,7 +23,7 @@ public function beatmapTags(): HasMany return $this->hasMany(BeatmapTag::class); } - public static function topTags($beatmapId) + public static function topTagIds(int $beatmapId, int $limit = 50) { return static ::joinRelation( @@ -31,11 +31,11 @@ public static function topTags($beatmapId) fn ($q) => $q->where('beatmap_id', $beatmapId)->whereHas('user', fn ($userQuery) => $userQuery->default()) ) ->groupBy('id') - ->select('id', 'name') + ->select('id') ->selectRaw('COUNT(*) as count') ->orderBy('count', 'desc') ->orderBy('id', 'desc') - ->limit(50) + ->limit($limit) ->get(); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index bd080bdcae9..7d358070636 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -31,6 +31,7 @@ class AppServiceProvider extends ServiceProvider 'layout-cache' => Singletons\LayoutCache::class, 'medals' => Singletons\Medals::class, 'smilies' => Singletons\Smilies::class, + 'tags' => Singletons\Tags::class, 'user-cover-presets' => Singletons\UserCoverPresets::class, ]; diff --git a/app/Singletons/Tags.php b/app/Singletons/Tags.php new file mode 100644 index 00000000000..b8cccbd6e46 --- /dev/null +++ b/app/Singletons/Tags.php @@ -0,0 +1,35 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +namespace App\Singletons; + +use App\Models\Tag; +use App\Traits\Memoizes; +use App\Transformers\TagTransformer; + +class Tags +{ + use Memoizes; + + public function get(int $id): ?Tag + { + $allById = $this->memoize( + 'allById', + fn () => Tag::all()->keyBy('id'), + ); + + return $allById[$id] ?? null; + } + + public function json(): array + { + return $this->memoize( + __FUNCTION__, + fn () => json_collection(Tag::all(), new TagTransformer()), + ); + } +} From 4a55d98edebb9e5041115a33e339c986d6d73076 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Fri, 13 Dec 2024 15:38:29 +0900 Subject: [PATCH 009/153] return tags with counts with beatmaps --- app/Http/Controllers/BeatmapsetsController.php | 2 +- app/Transformers/BeatmapCompactTransformer.php | 6 ++++++ resources/js/beatmapsets-show/controller.ts | 18 ++++++++++++++++++ resources/js/beatmapsets-show/info.tsx | 9 +++++++-- resources/js/interfaces/beatmap-json.ts | 2 ++ .../js/interfaces/beatmapset-extended-json.ts | 1 - resources/js/interfaces/tag-json.ts | 1 + 7 files changed, 35 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/BeatmapsetsController.php b/app/Http/Controllers/BeatmapsetsController.php index d8f91d7b5ad..692f089fda3 100644 --- a/app/Http/Controllers/BeatmapsetsController.php +++ b/app/Http/Controllers/BeatmapsetsController.php @@ -404,6 +404,7 @@ private function showJson($beatmapset) 'beatmaps.failtimes', 'beatmaps.max_combo', 'beatmaps.owners', + 'beatmaps.tags', 'converts', 'converts.failtimes', 'converts.owners', @@ -417,7 +418,6 @@ private function showJson($beatmapset) 'recent_favourites', 'related_users', 'user', - 'user_tags', ]); } } diff --git a/app/Transformers/BeatmapCompactTransformer.php b/app/Transformers/BeatmapCompactTransformer.php index ef8a96c6542..32cad40ce36 100644 --- a/app/Transformers/BeatmapCompactTransformer.php +++ b/app/Transformers/BeatmapCompactTransformer.php @@ -18,6 +18,7 @@ class BeatmapCompactTransformer extends TransformerAbstract 'failtimes', 'max_combo', 'owners', + 'tags', 'user', ]; @@ -83,6 +84,11 @@ public function includeOwners(Beatmap $beatmap) ]); } + public function includeTags(Beatmap $beatmap) + { + return $this->primitive($beatmap->topTagsJson()); + } + public function includeUser(Beatmap $beatmap) { return $this->item( diff --git a/resources/js/beatmapsets-show/controller.ts b/resources/js/beatmapsets-show/controller.ts index 7447723e6cf..54b2fb2f0ea 100644 --- a/resources/js/beatmapsets-show/controller.ts +++ b/resources/js/beatmapsets-show/controller.ts @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. // See the LICENCE file in the repository root for full licence text. +import BeatmapJson from 'interfaces/beatmap-json'; import { BeatmapsetJsonForShow } from 'interfaces/beatmapset-extended-json'; import UserJson from 'interfaces/user-json'; import { keyBy } from 'lodash'; @@ -70,6 +71,23 @@ export default class Controller { return this.beatmaps.get(this.currentBeatmap.mode) ?? []; } + @computed + get tags() { + const summedTags: Partial> = {}; + for (const beatmap of this.beatmapset.beatmaps) { + if (beatmap.tags == null) continue; + for (const tag of beatmap.tags) { + if (summedTags[tag.id] != null) { + summedTags[tag.id].count += tag.count; + } else { + summedTags[tag.id] = tag; + } + } + } + + return summedTags; + } + @computed get usersById() { return keyBy(this.beatmapset.related_users, 'id') as Partial>; diff --git a/resources/js/beatmapsets-show/info.tsx b/resources/js/beatmapsets-show/info.tsx index e1974cbda50..97c5b1dafea 100644 --- a/resources/js/beatmapsets-show/info.tsx +++ b/resources/js/beatmapsets-show/info.tsx @@ -66,9 +66,14 @@ export default class Info extends React.Component { } private get tags() { + const sortedTags = Object.values(this.controller.tags).sort((a, b) => { + const diff = b.count - a.count; + return diff !== 0 ? diff : a.id - b.id; + }); + return [ - ...this.controller.beatmapset.user_tags.map((tag) => tag.name), - ...this.controller.beatmapset.tags + ...sortedTags.map((tag) => tag.name), + ...this.controller.beatmapset.tags // TODO: something about duplicate mapper tags .split(' ') .filter(present), ]; diff --git a/resources/js/interfaces/beatmap-json.ts b/resources/js/interfaces/beatmap-json.ts index a7d40211981..ee6db218529 100644 --- a/resources/js/interfaces/beatmap-json.ts +++ b/resources/js/interfaces/beatmap-json.ts @@ -4,6 +4,7 @@ import BeatmapOwnerJson from './beatmap-owner-json'; import BeatmapsetJson from './beatmapset-json'; import Ruleset from './ruleset'; +import TagJson from './tag-json'; import UserJson from './user-json'; interface BeatmapFailTimesArray { @@ -17,6 +18,7 @@ interface BeatmapJsonAvailableIncludes { failtimes: BeatmapFailTimesArray; max_combo: number; owners: BeatmapOwnerJson[]; + tags: (TagJson & Required>)[]; user: UserJson; } diff --git a/resources/js/interfaces/beatmapset-extended-json.ts b/resources/js/interfaces/beatmapset-extended-json.ts index 20f2f19a9e6..1a41e2c9d3c 100644 --- a/resources/js/interfaces/beatmapset-extended-json.ts +++ b/resources/js/interfaces/beatmapset-extended-json.ts @@ -57,7 +57,6 @@ type BeatmapsetJsonForShowIncludes = Required>; export type BeatmapsetJsonForShow = diff --git a/resources/js/interfaces/tag-json.ts b/resources/js/interfaces/tag-json.ts index 5d1352a7a4b..557b39d4b8b 100644 --- a/resources/js/interfaces/tag-json.ts +++ b/resources/js/interfaces/tag-json.ts @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. export default interface TagJson { + count?: number; description: string; id: number; name: string; From b3fcdfa9b1b4f2fc3c8761381aa99077ccf4e5d3 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Fri, 13 Dec 2024 16:56:04 +0900 Subject: [PATCH 010/153] add TagJsonWithCount type --- resources/js/beatmapsets-show/controller.ts | 14 ++++++++------ resources/js/interfaces/beatmap-json.ts | 4 ++-- resources/js/interfaces/tag-json.ts | 2 ++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/resources/js/beatmapsets-show/controller.ts b/resources/js/beatmapsets-show/controller.ts index 54b2fb2f0ea..b3d1ab36681 100644 --- a/resources/js/beatmapsets-show/controller.ts +++ b/resources/js/beatmapsets-show/controller.ts @@ -1,8 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. // See the LICENCE file in the repository root for full licence text. -import BeatmapJson from 'interfaces/beatmap-json'; import { BeatmapsetJsonForShow } from 'interfaces/beatmapset-extended-json'; +import { TagJsonWithCount } from 'interfaces/tag-json'; import UserJson from 'interfaces/user-json'; import { keyBy } from 'lodash'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; @@ -73,15 +73,17 @@ export default class Controller { @computed get tags() { - const summedTags: Partial> = {}; + const summedTags: Partial> = {}; for (const beatmap of this.beatmapset.beatmaps) { if (beatmap.tags == null) continue; + for (const tag of beatmap.tags) { - if (summedTags[tag.id] != null) { - summedTags[tag.id].count += tag.count; - } else { - summedTags[tag.id] = tag; + const summedTag = summedTags[tag.id]; + if (summedTag != null) { + summedTag.count += tag.count; } + + summedTags[tag.id] = summedTag; } } diff --git a/resources/js/interfaces/beatmap-json.ts b/resources/js/interfaces/beatmap-json.ts index ee6db218529..8400706c956 100644 --- a/resources/js/interfaces/beatmap-json.ts +++ b/resources/js/interfaces/beatmap-json.ts @@ -4,7 +4,7 @@ import BeatmapOwnerJson from './beatmap-owner-json'; import BeatmapsetJson from './beatmapset-json'; import Ruleset from './ruleset'; -import TagJson from './tag-json'; +import { TagJsonWithCount } from './tag-json'; import UserJson from './user-json'; interface BeatmapFailTimesArray { @@ -18,7 +18,7 @@ interface BeatmapJsonAvailableIncludes { failtimes: BeatmapFailTimesArray; max_combo: number; owners: BeatmapOwnerJson[]; - tags: (TagJson & Required>)[]; + tags: TagJsonWithCount[]; user: UserJson; } diff --git a/resources/js/interfaces/tag-json.ts b/resources/js/interfaces/tag-json.ts index 557b39d4b8b..da857843edc 100644 --- a/resources/js/interfaces/tag-json.ts +++ b/resources/js/interfaces/tag-json.ts @@ -7,3 +7,5 @@ export default interface TagJson { id: number; name: string; } + +export type TagJsonWithCount = TagJson & Required>; From d88307554c57a4b441deea93fa2c87e65ad8ec62 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Fri, 13 Dec 2024 18:55:35 +0900 Subject: [PATCH 011/153] merge user and mapper tags, place mapper tags at the end --- resources/js/beatmapsets-show/controller.ts | 25 ++++++++++++++++----- resources/js/beatmapsets-show/info.tsx | 11 ++++----- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/resources/js/beatmapsets-show/controller.ts b/resources/js/beatmapsets-show/controller.ts index b3d1ab36681..542d6c251b8 100644 --- a/resources/js/beatmapsets-show/controller.ts +++ b/resources/js/beatmapsets-show/controller.ts @@ -5,12 +5,13 @@ import { BeatmapsetJsonForShow } from 'interfaces/beatmapset-extended-json'; import { TagJsonWithCount } from 'interfaces/tag-json'; import UserJson from 'interfaces/user-json'; import { keyBy } from 'lodash'; -import { action, computed, makeObservable, observable, runInAction } from 'mobx'; +import { action, computed, makeObservable, observable, runInAction, toJS } from 'mobx'; import { deletedUserJson } from 'models/user'; import core from 'osu-core-singleton'; import { find, findDefault, group } from 'utils/beatmap-helper'; import { parse } from 'utils/beatmapset-page-hash'; import { parseJson } from 'utils/json'; +import { present } from 'utils/string'; import { currentUrl } from 'utils/turbolinks'; export type ScoreLoadingState = null | 'error' | 'loading' | 'supporter_only' | 'unranked'; @@ -73,21 +74,33 @@ export default class Controller { @computed get tags() { - const summedTags: Partial> = {}; + const mapperTagSet = new Set(this.beatmapset.tags.split(' ').filter(present)); + + const userTags: Partial> = {}; for (const beatmap of this.beatmapset.beatmaps) { if (beatmap.tags == null) continue; for (const tag of beatmap.tags) { - const summedTag = summedTags[tag.id]; - if (summedTag != null) { + let summedTag = userTags[tag.id]; + if (summedTag == null) { + summedTag = toJS(tag); // don't modify original + userTags[tag.id] = summedTag; + } else { summedTag.count += tag.count; } - summedTags[tag.id] = summedTag; + // TODO: case insensitivity + if (mapperTagSet.has(tag.name)) { + summedTag.count++; + mapperTagSet.delete(tag.name); + } } } - return summedTags; + return { + mapperTags: [...mapperTagSet.values()], + userTags, + }; } @computed diff --git a/resources/js/beatmapsets-show/info.tsx b/resources/js/beatmapsets-show/info.tsx index 97c5b1dafea..a9c539039ed 100644 --- a/resources/js/beatmapsets-show/info.tsx +++ b/resources/js/beatmapsets-show/info.tsx @@ -66,16 +66,17 @@ export default class Info extends React.Component { } private get tags() { - const sortedTags = Object.values(this.controller.tags).sort((a, b) => { + const tags = this.controller.tags; + + const sortedUserTags = Object.values(tags.userTags).sort((a, b) => { + if (a == null || b == null) return 0; // for typing only, doesn't contain nulls. const diff = b.count - a.count; return diff !== 0 ? diff : a.id - b.id; }); return [ - ...sortedTags.map((tag) => tag.name), - ...this.controller.beatmapset.tags // TODO: something about duplicate mapper tags - .split(' ') - .filter(present), + ...sortedUserTags.map((tag) => tag?.name), + ...tags.mapperTags, ]; } From 3c9a6f5fbee35823f67ee1c7e4f4488d4952a89b Mon Sep 17 00:00:00 2001 From: bakaneko Date: Fri, 13 Dec 2024 20:49:04 +0900 Subject: [PATCH 012/153] do the sorting in controller --- resources/js/beatmapsets-show/controller.ts | 12 ++++++++---- resources/js/beatmapsets-show/info.tsx | 8 +------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/resources/js/beatmapsets-show/controller.ts b/resources/js/beatmapsets-show/controller.ts index 542d6c251b8..05e784e6349 100644 --- a/resources/js/beatmapsets-show/controller.ts +++ b/resources/js/beatmapsets-show/controller.ts @@ -76,15 +76,15 @@ export default class Controller { get tags() { const mapperTagSet = new Set(this.beatmapset.tags.split(' ').filter(present)); - const userTags: Partial> = {}; + const tags: Partial> = {}; for (const beatmap of this.beatmapset.beatmaps) { if (beatmap.tags == null) continue; for (const tag of beatmap.tags) { - let summedTag = userTags[tag.id]; + let summedTag = tags[tag.id]; if (summedTag == null) { summedTag = toJS(tag); // don't modify original - userTags[tag.id] = summedTag; + tags[tag.id] = summedTag; } else { summedTag.count += tag.count; } @@ -99,7 +99,11 @@ export default class Controller { return { mapperTags: [...mapperTagSet.values()], - userTags, + userTags: Object.values(tags).sort((a, b) => { + if (a == null || b == null) return 0; // for typing only, doesn't contain nulls. + const diff = b.count - a.count; + return diff !== 0 ? diff : a.id - b.id; + }), }; } diff --git a/resources/js/beatmapsets-show/info.tsx b/resources/js/beatmapsets-show/info.tsx index a9c539039ed..56cbe3641fc 100644 --- a/resources/js/beatmapsets-show/info.tsx +++ b/resources/js/beatmapsets-show/info.tsx @@ -68,14 +68,8 @@ export default class Info extends React.Component { private get tags() { const tags = this.controller.tags; - const sortedUserTags = Object.values(tags.userTags).sort((a, b) => { - if (a == null || b == null) return 0; // for typing only, doesn't contain nulls. - const diff = b.count - a.count; - return diff !== 0 ? diff : a.id - b.id; - }); - return [ - ...sortedUserTags.map((tag) => tag?.name), + ...tags.userTags.map((tag) => tag?.name), ...tags.mapperTags, ]; } From 1503ceb9fe3b1080d56b23adc005a63aa93a9e22 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 17 Dec 2024 19:16:39 +0900 Subject: [PATCH 013/153] lint --- app/Http/Controllers/TagsController.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/Http/Controllers/TagsController.php b/app/Http/Controllers/TagsController.php index a93ddeff028..b0ce5c493a9 100644 --- a/app/Http/Controllers/TagsController.php +++ b/app/Http/Controllers/TagsController.php @@ -7,9 +7,6 @@ namespace App\Http\Controllers; -use App\Models\Tag; -use App\Transformers\TagTransformer; - class TagsController extends Controller { public function __construct() From f5552af474b41005cec5c8e60ab54dd0772d9de6 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 17 Dec 2024 20:16:20 +0900 Subject: [PATCH 014/153] return id and counts seperately from full tag --- .../Controllers/BeatmapsetsController.php | 3 +- app/Models/Beatmap.php | 20 +-------- .../BeatmapCompactTransformer.php | 6 +-- .../BeatmapsetCompactTransformer.php | 17 ++++++-- resources/js/beatmapsets-show/controller.ts | 41 ++++++++++++------- resources/js/interfaces/beatmap-json.ts | 3 +- .../js/interfaces/beatmapset-extended-json.ts | 1 + resources/js/interfaces/beatmapset-json.ts | 1 + 8 files changed, 50 insertions(+), 42 deletions(-) diff --git a/app/Http/Controllers/BeatmapsetsController.php b/app/Http/Controllers/BeatmapsetsController.php index 692f089fda3..a2da6fc6b37 100644 --- a/app/Http/Controllers/BeatmapsetsController.php +++ b/app/Http/Controllers/BeatmapsetsController.php @@ -404,7 +404,7 @@ private function showJson($beatmapset) 'beatmaps.failtimes', 'beatmaps.max_combo', 'beatmaps.owners', - 'beatmaps.tags', + 'beatmaps.top_tag_ids', 'converts', 'converts.failtimes', 'converts.owners', @@ -416,6 +416,7 @@ private function showJson($beatmapset) 'pack_tags', 'ratings', 'recent_favourites', + 'related_tags', 'related_users', 'user', ]); diff --git a/app/Models/Beatmap.php b/app/Models/Beatmap.php index a042eebcf9c..57f18ae0aaa 100644 --- a/app/Models/Beatmap.php +++ b/app/Models/Beatmap.php @@ -348,30 +348,14 @@ public function status() return array_search($this->approved, Beatmapset::STATES, true); } - public function topTagsJson() + public function topTagIds() { - $tagIds = cache_remember_mutexed( + return cache_remember_mutexed( "beatmap_top_tag_ids:{$this->getKey()}", $GLOBALS['cfg']['osu']['tags']['beatmap_tags_cache_duration'], [], fn () => Tag::topTagIds($this->getKey())->toArray(), ); - - $cachedTags = app('tags'); - $json = []; - - foreach ($tagIds as $tagId) { - $tag = $cachedTags->get($tagId['id']); - if ($tag !== null) { - $json[] = [ - ...$tagId, - 'description' => $tag->description, - 'name' => $tag->name, - ]; - } - } - - return $json; } private function getDifficultyrating() diff --git a/app/Transformers/BeatmapCompactTransformer.php b/app/Transformers/BeatmapCompactTransformer.php index 32cad40ce36..9ec4aa198c3 100644 --- a/app/Transformers/BeatmapCompactTransformer.php +++ b/app/Transformers/BeatmapCompactTransformer.php @@ -18,7 +18,7 @@ class BeatmapCompactTransformer extends TransformerAbstract 'failtimes', 'max_combo', 'owners', - 'tags', + 'top_tag_ids', 'user', ]; @@ -84,9 +84,9 @@ public function includeOwners(Beatmap $beatmap) ]); } - public function includeTags(Beatmap $beatmap) + public function includeTopTagIds(Beatmap $beatmap) { - return $this->primitive($beatmap->topTagsJson()); + return $this->primitive($beatmap->topTagIds()); } public function includeUser(Beatmap $beatmap) diff --git a/app/Transformers/BeatmapsetCompactTransformer.php b/app/Transformers/BeatmapsetCompactTransformer.php index fd3f7d0694c..59f47b44d71 100644 --- a/app/Transformers/BeatmapsetCompactTransformer.php +++ b/app/Transformers/BeatmapsetCompactTransformer.php @@ -40,8 +40,8 @@ class BeatmapsetCompactTransformer extends TransformerAbstract 'ratings', 'recent_favourites', 'related_users', + 'related_tags', 'user', - 'user_tags', ]; // TODO: switch to enum after php 8.1 @@ -300,11 +300,22 @@ public function includeRelatedUsers(Beatmapset $beatmapset) return $this->collection($users, new UserCompactTransformer()); } - public function includeUserTags(Beatmapset $beatmapset) + public function includeRelatedTags(Beatmapset $beatmapset) { $beatmaps = $this->beatmaps($beatmapset); + $tagIdSet = new Set($beatmaps->flatMap->topTagIds()->pluck('id')); - return $this->collection($beatmaps->flatMap->topTags(), new TagTransformer()); + $cachedTags = app('tags'); + $json = []; + + foreach ($tagIdSet as $tagId) { + $tag = $cachedTags->get($tagId); + if ($tag !== null) { + $json[] = $tag; + } + } + + return $this->primitive($json); } private function beatmaps(Beatmapset $beatmapset, ?Fractal\ParamBag $params = null): EloquentCollection diff --git a/resources/js/beatmapsets-show/controller.ts b/resources/js/beatmapsets-show/controller.ts index 05e784e6349..e93119133f3 100644 --- a/resources/js/beatmapsets-show/controller.ts +++ b/resources/js/beatmapsets-show/controller.ts @@ -5,7 +5,7 @@ import { BeatmapsetJsonForShow } from 'interfaces/beatmapset-extended-json'; import { TagJsonWithCount } from 'interfaces/tag-json'; import UserJson from 'interfaces/user-json'; import { keyBy } from 'lodash'; -import { action, computed, makeObservable, observable, runInAction, toJS } from 'mobx'; +import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { deletedUserJson } from 'models/user'; import core from 'osu-core-singleton'; import { find, findDefault, group } from 'utils/beatmap-helper'; @@ -72,26 +72,38 @@ export default class Controller { return this.beatmaps.get(this.currentBeatmap.mode) ?? []; } + get relatedTags() { + const map = new Map(); + + for (const tag of this.beatmapset.related_tags) { + map.set(tag.id, tag); + } + + return map; + } + @computed get tags() { const mapperTagSet = new Set(this.beatmapset.tags.split(' ').filter(present)); + const tagMap = new Map(); + + for (const tag of this.beatmapset.related_tags) { + tag.count = 0; // assign 0 and cast + tagMap.set(tag.id, tag as TagJsonWithCount); + } - const tags: Partial> = {}; for (const beatmap of this.beatmapset.beatmaps) { - if (beatmap.tags == null) continue; - - for (const tag of beatmap.tags) { - let summedTag = tags[tag.id]; - if (summedTag == null) { - summedTag = toJS(tag); // don't modify original - tags[tag.id] = summedTag; - } else { - summedTag.count += tag.count; - } + if (beatmap.top_tag_ids == null) continue; + + for (const tagId of beatmap.top_tag_ids) { + const tag = tagMap.get(tagId.id); + if (tag == null) continue; + + tag.count += tagId.count; // TODO: case insensitivity if (mapperTagSet.has(tag.name)) { - summedTag.count++; + tag.count++; mapperTagSet.delete(tag.name); } } @@ -99,8 +111,7 @@ export default class Controller { return { mapperTags: [...mapperTagSet.values()], - userTags: Object.values(tags).sort((a, b) => { - if (a == null || b == null) return 0; // for typing only, doesn't contain nulls. + userTags: [...tagMap.values()].sort((a, b) => { const diff = b.count - a.count; return diff !== 0 ? diff : a.id - b.id; }), diff --git a/resources/js/interfaces/beatmap-json.ts b/resources/js/interfaces/beatmap-json.ts index 8400706c956..9c5837a51ea 100644 --- a/resources/js/interfaces/beatmap-json.ts +++ b/resources/js/interfaces/beatmap-json.ts @@ -4,7 +4,6 @@ import BeatmapOwnerJson from './beatmap-owner-json'; import BeatmapsetJson from './beatmapset-json'; import Ruleset from './ruleset'; -import { TagJsonWithCount } from './tag-json'; import UserJson from './user-json'; interface BeatmapFailTimesArray { @@ -18,7 +17,7 @@ interface BeatmapJsonAvailableIncludes { failtimes: BeatmapFailTimesArray; max_combo: number; owners: BeatmapOwnerJson[]; - tags: TagJsonWithCount[]; + top_tag_ids: { count: number; id: number }[]; user: UserJson; } diff --git a/resources/js/interfaces/beatmapset-extended-json.ts b/resources/js/interfaces/beatmapset-extended-json.ts index 1a41e2c9d3c..c7ff2f4038d 100644 --- a/resources/js/interfaces/beatmapset-extended-json.ts +++ b/resources/js/interfaces/beatmapset-extended-json.ts @@ -55,6 +55,7 @@ type BeatmapsetJsonForShowIncludes = Required>; diff --git a/resources/js/interfaces/beatmapset-json.ts b/resources/js/interfaces/beatmapset-json.ts index cb59fc13390..6519a014d1d 100644 --- a/resources/js/interfaces/beatmapset-json.ts +++ b/resources/js/interfaces/beatmapset-json.ts @@ -93,6 +93,7 @@ interface BeatmapsetJsonAvailableIncludes { nominations: BeatmapsetNominationsInterface; ratings: number[]; recent_favourites: UserJson[]; + related_tags: TagJson[]; related_users: UserJson[]; user: UserJson | UserJsonDeleted; user_tags: TagJson[]; From 5ad134a1904fa3c99e615d26df84a6f23c594d6b Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 17 Dec 2024 22:11:43 +0900 Subject: [PATCH 015/153] remove route (get from beatmapset instead) --- app/Http/Controllers/BeatmapTagsController.php | 9 --------- routes/web.php | 2 +- tests/Controllers/BeatmapTagsControllerTest.php | 16 ---------------- tests/api_routes.json | 16 ---------------- 4 files changed, 1 insertion(+), 42 deletions(-) diff --git a/app/Http/Controllers/BeatmapTagsController.php b/app/Http/Controllers/BeatmapTagsController.php index 630010f7564..f6cf56b035a 100644 --- a/app/Http/Controllers/BeatmapTagsController.php +++ b/app/Http/Controllers/BeatmapTagsController.php @@ -23,15 +23,6 @@ public function __construct() 'destroy', ], ]); - - $this->middleware('require-scopes:public', ['only' => 'index']); - } - - public function index($beatmapId) - { - return [ - 'beatmap_tags' => Beatmap::findOrFail($beatmapId)->topTagsJson(), - ]; } public function destroy($beatmapId, $tagId) diff --git a/routes/web.php b/routes/web.php index a94e8853fa3..9b3bfd6151e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -430,7 +430,7 @@ }); }); - Route::apiResource('tags', 'BeatmapTagsController', ['only' => ['index', 'store', 'destroy']]); + Route::apiResource('tags', 'BeatmapTagsController', ['only' => ['store', 'destroy']]); }); }); diff --git a/tests/Controllers/BeatmapTagsControllerTest.php b/tests/Controllers/BeatmapTagsControllerTest.php index 84b17bd570f..f75438c1454 100644 --- a/tests/Controllers/BeatmapTagsControllerTest.php +++ b/tests/Controllers/BeatmapTagsControllerTest.php @@ -12,7 +12,6 @@ use App\Models\Solo\Score; use App\Models\Tag; use App\Models\User; -use Illuminate\Testing\Fluent\AssertableJson; use Tests\TestCase; class BeatmapTagsControllerTest extends TestCase @@ -21,21 +20,6 @@ class BeatmapTagsControllerTest extends TestCase private Beatmap $beatmap; private BeatmapTag $beatmapTag; - public function testIndex(): void - { - $this->actAsScopedUser(User::factory()->create(), ['public']); - - $this - ->get(route('api.beatmaps.tags.index', ['beatmap' => $this->beatmap->getKey()])) - ->assertSuccessful() - ->assertJson(fn (AssertableJson $json) => - $json - ->where('beatmap_tags.0.id', $this->tag->getKey()) - ->where('beatmap_tags.0.name', $this->tag->name) - ->where('beatmap_tags.0.count', 1) - ->etc()); - } - public function testStore(): void { $user = User::factory() diff --git a/tests/api_routes.json b/tests/api_routes.json index 68fe32aac33..007f537198f 100644 --- a/tests/api_routes.json +++ b/tests/api_routes.json @@ -173,22 +173,6 @@ ], "scopes": [] }, - { - "uri": "api/v2/beatmaps/{beatmap}/tags", - "methods": [ - "GET", - "HEAD" - ], - "controller": "App\\Http\\Controllers\\BeatmapTagsController@index", - "middlewares": [ - "App\\Http\\Middleware\\ThrottleRequests:1200,1,api:", - "App\\Http\\Middleware\\RequireScopes", - "App\\Http\\Middleware\\RequireScopes:public" - ], - "scopes": [ - "public" - ] - }, { "uri": "api/v2/beatmaps/{beatmap}/tags", "methods": [ From 3c9c2e51c20ec395525c7160fd8c8b0895f4b651 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 17 Dec 2024 22:19:33 +0900 Subject: [PATCH 016/153] not nullable --- resources/js/beatmapsets-show/info.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/js/beatmapsets-show/info.tsx b/resources/js/beatmapsets-show/info.tsx index 56cbe3641fc..b5e3eadb3e1 100644 --- a/resources/js/beatmapsets-show/info.tsx +++ b/resources/js/beatmapsets-show/info.tsx @@ -69,7 +69,7 @@ export default class Info extends React.Component { const tags = this.controller.tags; return [ - ...tags.userTags.map((tag) => tag?.name), + ...tags.userTags.map((tag) => tag.name), ...tags.mapperTags, ]; } From 9e01f3d8ba725393395677f3c5740a02e92b5362 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 18 Dec 2024 15:27:59 +0900 Subject: [PATCH 017/153] don't need the extra join/relation anymore; make into query --- app/Models/Beatmap.php | 2 +- app/Models/BeatmapTag.php | 12 ++++++++++++ app/Models/Tag.php | 16 ---------------- .../BeatmapsetCompactTransformer.php | 2 +- resources/js/beatmapsets-show/controller.ts | 2 +- resources/js/interfaces/beatmap-json.ts | 2 +- 6 files changed, 16 insertions(+), 20 deletions(-) diff --git a/app/Models/Beatmap.php b/app/Models/Beatmap.php index 57f18ae0aaa..54205677699 100644 --- a/app/Models/Beatmap.php +++ b/app/Models/Beatmap.php @@ -354,7 +354,7 @@ public function topTagIds() "beatmap_top_tag_ids:{$this->getKey()}", $GLOBALS['cfg']['osu']['tags']['beatmap_tags_cache_duration'], [], - fn () => Tag::topTagIds($this->getKey())->toArray(), + fn () => BeatmapTag::topTagIdsQuery($this->getKey())->get()->toArray(), ); } diff --git a/app/Models/BeatmapTag.php b/app/Models/BeatmapTag.php index a4fca351852..8b1cf46fd17 100644 --- a/app/Models/BeatmapTag.php +++ b/app/Models/BeatmapTag.php @@ -19,6 +19,18 @@ class BeatmapTag extends Model protected $primaryKey = ':composite'; protected $primaryKeys = ['beatmap_id', 'tag_id', 'user_id']; + public static function topTagIdsQuery(int $beatmapId, int $limit = 50) + { + return static::where('beatmap_id', $beatmapId) + ->whereHas('user', fn ($userQuery) => $userQuery->default()) + ->groupBy('tag_id') + ->select('tag_id') + ->selectRaw('COUNT(*) as count') + ->orderBy('count', 'desc') + ->orderBy('tag_id', 'asc') + ->limit($limit); + } + public function beatmap() { return $this->belongsTo(Beatmap::class, 'beatmap_id'); diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 74293c5c00c..92134fa99ad 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -22,20 +22,4 @@ public function beatmapTags(): HasMany { return $this->hasMany(BeatmapTag::class); } - - public static function topTagIds(int $beatmapId, int $limit = 50) - { - return static - ::joinRelation( - 'beatmapTags', - fn ($q) => $q->where('beatmap_id', $beatmapId)->whereHas('user', fn ($userQuery) => $userQuery->default()) - ) - ->groupBy('id') - ->select('id') - ->selectRaw('COUNT(*) as count') - ->orderBy('count', 'desc') - ->orderBy('id', 'desc') - ->limit($limit) - ->get(); - } } diff --git a/app/Transformers/BeatmapsetCompactTransformer.php b/app/Transformers/BeatmapsetCompactTransformer.php index 59f47b44d71..f4f95e18cfd 100644 --- a/app/Transformers/BeatmapsetCompactTransformer.php +++ b/app/Transformers/BeatmapsetCompactTransformer.php @@ -303,7 +303,7 @@ public function includeRelatedUsers(Beatmapset $beatmapset) public function includeRelatedTags(Beatmapset $beatmapset) { $beatmaps = $this->beatmaps($beatmapset); - $tagIdSet = new Set($beatmaps->flatMap->topTagIds()->pluck('id')); + $tagIdSet = new Set($beatmaps->flatMap->topTagIds()->pluck('tag_id')); $cachedTags = app('tags'); $json = []; diff --git a/resources/js/beatmapsets-show/controller.ts b/resources/js/beatmapsets-show/controller.ts index e93119133f3..04ec53a20a5 100644 --- a/resources/js/beatmapsets-show/controller.ts +++ b/resources/js/beatmapsets-show/controller.ts @@ -96,7 +96,7 @@ export default class Controller { if (beatmap.top_tag_ids == null) continue; for (const tagId of beatmap.top_tag_ids) { - const tag = tagMap.get(tagId.id); + const tag = tagMap.get(tagId.tag_id); if (tag == null) continue; tag.count += tagId.count; diff --git a/resources/js/interfaces/beatmap-json.ts b/resources/js/interfaces/beatmap-json.ts index 9c5837a51ea..f00b9b24a14 100644 --- a/resources/js/interfaces/beatmap-json.ts +++ b/resources/js/interfaces/beatmap-json.ts @@ -17,7 +17,7 @@ interface BeatmapJsonAvailableIncludes { failtimes: BeatmapFailTimesArray; max_combo: number; owners: BeatmapOwnerJson[]; - top_tag_ids: { count: number; id: number }[]; + top_tag_ids: { count: number; tag_id: number }[]; user: UserJson; } From 715db0347cc4256d0a453dc23fe5d2d3bf983fc7 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 18 Dec 2024 15:41:00 +0900 Subject: [PATCH 018/153] copy and return typed value instead of messing with original object --- resources/js/beatmapsets-show/controller.ts | 15 ++++++++++++--- resources/js/interfaces/tag-json.ts | 3 --- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/resources/js/beatmapsets-show/controller.ts b/resources/js/beatmapsets-show/controller.ts index 04ec53a20a5..c4fc7f1fd53 100644 --- a/resources/js/beatmapsets-show/controller.ts +++ b/resources/js/beatmapsets-show/controller.ts @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. import { BeatmapsetJsonForShow } from 'interfaces/beatmapset-extended-json'; -import { TagJsonWithCount } from 'interfaces/tag-json'; +import TagJson from 'interfaces/tag-json'; import UserJson from 'interfaces/user-json'; import { keyBy } from 'lodash'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; @@ -25,6 +25,16 @@ interface State { showingNsfwWarning: boolean; } +type TagJsonWithCount = TagJson & { count: number }; + +function asTagJsonWithCount(tag: TagJson) { + return { + count: 0, + ...tag, + }; +} + + export default class Controller { @observable hoveredBeatmap: null | BeatmapJsonForBeatmapsetShow = null; @observable state: State; @@ -88,8 +98,7 @@ export default class Controller { const tagMap = new Map(); for (const tag of this.beatmapset.related_tags) { - tag.count = 0; // assign 0 and cast - tagMap.set(tag.id, tag as TagJsonWithCount); + tagMap.set(tag.id, asTagJsonWithCount(tag)); } for (const beatmap of this.beatmapset.beatmaps) { diff --git a/resources/js/interfaces/tag-json.ts b/resources/js/interfaces/tag-json.ts index da857843edc..5d1352a7a4b 100644 --- a/resources/js/interfaces/tag-json.ts +++ b/resources/js/interfaces/tag-json.ts @@ -2,10 +2,7 @@ // See the LICENCE file in the repository root for full licence text. export default interface TagJson { - count?: number; description: string; id: number; name: string; } - -export type TagJsonWithCount = TagJson & Required>; From dd68826c02a7a4de654abc86457a89993d5dfb3b Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 18 Dec 2024 21:02:44 +0900 Subject: [PATCH 019/153] don't double fetch from redis for transformer --- app/Models/Beatmap.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/app/Models/Beatmap.php b/app/Models/Beatmap.php index 54205677699..3cca72263e3 100644 --- a/app/Models/Beatmap.php +++ b/app/Models/Beatmap.php @@ -8,6 +8,7 @@ use App\Exceptions\InvariantException; use App\Jobs\EsDocument; use App\Libraries\Transactions\AfterCommit; +use App\Traits\Memoizes; use DB; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; @@ -53,7 +54,7 @@ */ class Beatmap extends Model implements AfterCommit { - use SoftDeletes; + use Memoizes, SoftDeletes; public $convert = false; @@ -350,11 +351,15 @@ public function status() public function topTagIds() { - return cache_remember_mutexed( - "beatmap_top_tag_ids:{$this->getKey()}", - $GLOBALS['cfg']['osu']['tags']['beatmap_tags_cache_duration'], - [], - fn () => BeatmapTag::topTagIdsQuery($this->getKey())->get()->toArray(), + // TODO: Add option to multi query when beatmapset requests all tags for beatmaps? + return $this->memoize( + __FUNCTION__, + fn () => cache_remember_mutexed( + "beatmap_top_tag_ids:{$this->getKey()}", + $GLOBALS['cfg']['osu']['tags']['beatmap_tags_cache_duration'], + [], + fn () => BeatmapTag::topTagIdsQuery($this->getKey())->get()->toArray(), + ), ); } From 2b3bb426650d18485bd32964b82a19ef87cf1d8a Mon Sep 17 00:00:00 2001 From: bakaneko Date: Thu, 19 Dec 2024 16:32:55 +0900 Subject: [PATCH 020/153] should be unique --- database/factories/TagFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/factories/TagFactory.php b/database/factories/TagFactory.php index f1bb27b044d..8617d60d431 100644 --- a/database/factories/TagFactory.php +++ b/database/factories/TagFactory.php @@ -16,7 +16,7 @@ class TagFactory extends Factory public function definition(): array { return [ - 'name' => fn () => "Tag {$this->faker->word}", + 'name' => fn () => "Tag {$this->faker->unique()->word}", 'description' => fn () => $this->faker->sentence, ]; } From 05db2c1f21bf2d951f43ab5d2fe779e0efbf7be1 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Thu, 19 Dec 2024 19:49:11 +0900 Subject: [PATCH 021/153] removed/unused --- resources/js/beatmapsets-show/controller.ts | 10 ---------- resources/js/interfaces/beatmapset-json.ts | 1 - 2 files changed, 11 deletions(-) diff --git a/resources/js/beatmapsets-show/controller.ts b/resources/js/beatmapsets-show/controller.ts index c4fc7f1fd53..3f2050db321 100644 --- a/resources/js/beatmapsets-show/controller.ts +++ b/resources/js/beatmapsets-show/controller.ts @@ -82,16 +82,6 @@ export default class Controller { return this.beatmaps.get(this.currentBeatmap.mode) ?? []; } - get relatedTags() { - const map = new Map(); - - for (const tag of this.beatmapset.related_tags) { - map.set(tag.id, tag); - } - - return map; - } - @computed get tags() { const mapperTagSet = new Set(this.beatmapset.tags.split(' ').filter(present)); diff --git a/resources/js/interfaces/beatmapset-json.ts b/resources/js/interfaces/beatmapset-json.ts index 6519a014d1d..f6203c3c052 100644 --- a/resources/js/interfaces/beatmapset-json.ts +++ b/resources/js/interfaces/beatmapset-json.ts @@ -96,7 +96,6 @@ interface BeatmapsetJsonAvailableIncludes { related_tags: TagJson[]; related_users: UserJson[]; user: UserJson | UserJsonDeleted; - user_tags: TagJson[]; } interface HypeData { From 0eefd0b23cd3da7217c286e2152130330f80a46c Mon Sep 17 00:00:00 2001 From: bakaneko Date: Thu, 19 Dec 2024 19:55:54 +0900 Subject: [PATCH 022/153] missing non-incrementing --- app/Models/BeatmapTag.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Models/BeatmapTag.php b/app/Models/BeatmapTag.php index 8b1cf46fd17..96a7be925ce 100644 --- a/app/Models/BeatmapTag.php +++ b/app/Models/BeatmapTag.php @@ -18,6 +18,7 @@ class BeatmapTag extends Model { protected $primaryKey = ':composite'; protected $primaryKeys = ['beatmap_id', 'tag_id', 'user_id']; + public $incrementing = false; public static function topTagIdsQuery(int $beatmapId, int $limit = 50) { From c9ad28dad76504b4a94430b4d03ead1fe48d0e6b Mon Sep 17 00:00:00 2001 From: bakaneko Date: Thu, 19 Dec 2024 20:01:41 +0900 Subject: [PATCH 023/153] memoize all --- app/Singletons/Tags.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/Singletons/Tags.php b/app/Singletons/Tags.php index b8cccbd6e46..589bad8b557 100644 --- a/app/Singletons/Tags.php +++ b/app/Singletons/Tags.php @@ -10,16 +10,25 @@ use App\Models\Tag; use App\Traits\Memoizes; use App\Transformers\TagTransformer; +use Illuminate\Support\Collection; class Tags { use Memoizes; + /** + * @return Collection + */ + public function all(): Collection + { + return $this->memoize(__FUNCTION__, fn () => Tag::all()); + } + public function get(int $id): ?Tag { $allById = $this->memoize( 'allById', - fn () => Tag::all()->keyBy('id'), + fn () => $this->all()->keyBy('id'), ); return $allById[$id] ?? null; @@ -29,7 +38,7 @@ public function json(): array { return $this->memoize( __FUNCTION__, - fn () => json_collection(Tag::all(), new TagTransformer()), + fn () => json_collection($this->all(), new TagTransformer()), ); } } From fc4b347e8716f26b63123254af06af3ab6ab6ac1 Mon Sep 17 00:00:00 2001 From: nanaya Date: Fri, 20 Dec 2024 16:51:55 +0900 Subject: [PATCH 024/153] Add disband team button --- app/Http/Controllers/TeamsController.php | 11 +++++++++++ app/Models/Team.php | 13 +++++++++++++ resources/lang/en/teams.php | 5 +++++ resources/views/teams/show.blade.php | 15 +++++++++++++++ routes/web.php | 2 +- 5 files changed, 45 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/TeamsController.php b/app/Http/Controllers/TeamsController.php index 0792c39db0b..92b6de5f8f8 100644 --- a/app/Http/Controllers/TeamsController.php +++ b/app/Http/Controllers/TeamsController.php @@ -19,6 +19,17 @@ public function __construct() $this->middleware('auth', ['only' => ['part']]); } + public function destroy(string $id): Response + { + $team = Team::findOrFail($id); + priv_check('TeamUpdate', $team)->ensureCan(); + + $team->delete(); + \Session::flash('popup', osu_trans('teams.destroy.ok')); + + return ujs_redirect(route('home')); + } + public function edit(string $id): Response { $team = Team::findOrFail($id); diff --git a/app/Models/Team.php b/app/Models/Team.php index a4a324052a3..7db9acd9690 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -76,6 +76,19 @@ public function descriptionHtml(): string : bbcode((new BBCodeForDB($description))->generate()); } + public function delete() + { + $ret = parent::delete(); + + if ($ret) { + $this->header()->delete(); + $this->logo()->delete(); + $this->members()->delete(); + } + + return $ret; + } + public function header(): Uploader { return $this->header ??= new Uploader( diff --git a/resources/lang/en/teams.php b/resources/lang/en/teams.php index ae637b63d52..a3984e14dff 100644 --- a/resources/lang/en/teams.php +++ b/resources/lang/en/teams.php @@ -4,6 +4,10 @@ // See the LICENCE file in the repository root for full licence text. return [ + 'destroy' => [ + 'ok' => 'Team removed', + ], + 'edit' => [ 'saved' => 'Settings saved successfully', 'title' => 'Team Settings', @@ -66,6 +70,7 @@ 'show' => [ 'bar' => [ + 'destroy' => 'Disband Team', 'part' => 'Leave Team', ], diff --git a/resources/views/teams/show.blade.php b/resources/views/teams/show.blade.php index ed4c2d6e2ce..ae9e5927d8e 100644 --- a/resources/views/teams/show.blade.php +++ b/resources/views/teams/show.blade.php @@ -18,6 +18,9 @@ if (priv_check('TeamPart', $team)->can()) { $buttons->add('part'); } + if (priv_check('TeamUpdate', $team)->can()) { + $buttons->add('destroy'); + } @endphp @extends('master', [ @@ -76,6 +79,18 @@ class="btn-circle btn-circle--page-toggle"
@if (!$buttons->isEmpty())
+ @if ($buttons->contains('destroy')) +
+ + +
+ @endif @if ($buttons->contains('part'))
name('part'); Route::resource('members', 'Teams\MembersController', ['only' => ['destroy', 'index']]); }); - Route::resource('teams', 'TeamsController', ['only' => ['edit', 'show', 'update']]); + Route::resource('teams', 'TeamsController', ['only' => ['destroy', 'edit', 'show', 'update']]); Route::post('users/check-username-availability', 'UsersController@checkUsernameAvailability')->name('users.check-username-availability'); Route::get('users/lookup', 'Users\LookupController@index')->name('users.lookup'); From 421504c8350250e4c5fcb20dac98e86eff2299b5 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Fri, 20 Dec 2024 23:11:06 +0900 Subject: [PATCH 025/153] add composite key test; add missing timestamp properties --- app/Models/BeatmapTag.php | 2 ++ tests/Models/ModelCompositePrimaryKeysTest.php | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/app/Models/BeatmapTag.php b/app/Models/BeatmapTag.php index 96a7be925ce..fc7abf63ce8 100644 --- a/app/Models/BeatmapTag.php +++ b/app/Models/BeatmapTag.php @@ -10,7 +10,9 @@ /** * @property-read Beatmap $beatmap * @property int $beatmap_id + * @property \Carbon\Carbon $created_at * @property int $tag_id + * @property \Carbon\Carbon $updated_at * @property-read User $user * @property int $user_id */ diff --git a/tests/Models/ModelCompositePrimaryKeysTest.php b/tests/Models/ModelCompositePrimaryKeysTest.php index d2e02b3edac..58d8b97575f 100644 --- a/tests/Models/ModelCompositePrimaryKeysTest.php +++ b/tests/Models/ModelCompositePrimaryKeysTest.php @@ -10,6 +10,7 @@ use App\Models\BeatmapDifficulty; use App\Models\BeatmapDifficultyAttrib; use App\Models\BeatmapFailtimes; +use App\Models\BeatmapTag; use App\Models\Chat; use App\Models\FavouriteBeatmapset; use App\Models\Forum; @@ -112,6 +113,16 @@ public static function dataProviderBase() ['type' => 'exit'], ['p1', [0, 10], 11], ], + [ + BeatmapTag::class, + [ + 'beatmap_id' => 0, + 'tag_id' => 0, + 'user_id' => 0, + ], + ['tag_id' => 1], + ['updated_at', [Carbon::now()->subDays(5), Carbon::now()->subDays(1)], Carbon::now()], + ], [ Chat\UserChannel::class, [ From efad0be5359e5d2508d5cb1e1915a3889c03a45a Mon Sep 17 00:00:00 2001 From: bakaneko Date: Sat, 21 Dec 2024 00:20:47 +0900 Subject: [PATCH 026/153] tie break with names --- resources/js/beatmapsets-show/controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/js/beatmapsets-show/controller.ts b/resources/js/beatmapsets-show/controller.ts index 3f2050db321..df472a63140 100644 --- a/resources/js/beatmapsets-show/controller.ts +++ b/resources/js/beatmapsets-show/controller.ts @@ -112,7 +112,7 @@ export default class Controller { mapperTags: [...mapperTagSet.values()], userTags: [...tagMap.values()].sort((a, b) => { const diff = b.count - a.count; - return diff !== 0 ? diff : a.id - b.id; + return diff !== 0 ? diff : a.name.localeCompare(b.name); }), }; } From cde18d06af8beaee20e4c780c3f3b43a7e309454 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Sat, 21 Dec 2024 00:22:36 +0900 Subject: [PATCH 027/153] don't dedupe mapper tags since they might be names --- resources/js/beatmapsets-show/controller.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/resources/js/beatmapsets-show/controller.ts b/resources/js/beatmapsets-show/controller.ts index df472a63140..15f9acc725d 100644 --- a/resources/js/beatmapsets-show/controller.ts +++ b/resources/js/beatmapsets-show/controller.ts @@ -84,7 +84,6 @@ export default class Controller { @computed get tags() { - const mapperTagSet = new Set(this.beatmapset.tags.split(' ').filter(present)); const tagMap = new Map(); for (const tag of this.beatmapset.related_tags) { @@ -99,17 +98,11 @@ export default class Controller { if (tag == null) continue; tag.count += tagId.count; - - // TODO: case insensitivity - if (mapperTagSet.has(tag.name)) { - tag.count++; - mapperTagSet.delete(tag.name); - } } } return { - mapperTags: [...mapperTagSet.values()], + mapperTags: this.beatmapset.tags.split(' ').filter(present), userTags: [...tagMap.values()].sort((a, b) => { const diff = b.count - a.count; return diff !== 0 ? diff : a.name.localeCompare(b.name); From 68c26056d2efa5ae988eca8259720e82e4d9aa07 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 23 Dec 2024 20:11:26 +0900 Subject: [PATCH 028/153] Allow multiplayer scores with arbitrary beatmap/ruleset combinations --- .../Multiplayer/Rooms/Playlist/ScoresController.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php b/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php index ea1f7321d1f..858305be77f 100644 --- a/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php +++ b/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php @@ -8,6 +8,7 @@ use App\Exceptions\InvariantException; use App\Http\Controllers\Controller as BaseController; use App\Libraries\ClientCheck; +use App\Models\Beatmap; use App\Models\Multiplayer\PlaylistItem; use App\Models\Multiplayer\PlaylistItemUserHighScore; use App\Models\Multiplayer\Room; @@ -184,8 +185,15 @@ public function store($roomId, $playlistId) $request = \Request::instance(); $params = $request->all(); - if (get_string($params['beatmap_hash'] ?? null) !== $playlistItem->beatmap->checksum) { - throw new InvariantException(osu_trans('score_tokens.create.beatmap_hash_invalid')); + if ($playlistItem->beatmapset_id === null) { + if (get_string($params['beatmap_hash'] ?? null) !== $playlistItem->beatmap->checksum) { + throw new InvariantException(osu_trans('score_tokens.create.beatmap_hash_invalid')); + } + } else { + // Todo: Validate beatmap_hash param matches any checksum from the playlist item's beatmap set. + // Todo: Modifying the playlist item looks dodgy to me and is likely failing some internal validations. + $playlistItem->beatmap_id = Beatmap::firstWhere('checksum', get_string($params['beatmap_hash'] ?? null))->beatmap_id; + $playlistItem->ruleset_id = get_int($params['ruleset_id'] ?? null); } $buildId = ClientCheck::parseToken($request)['buildId']; From 572b0461c9c5906a67def30741526111afb3c837 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 25 Dec 2024 08:02:38 +0900 Subject: [PATCH 029/153] show user tags per beatmap --- resources/js/beatmapsets-show/controller.ts | 27 +++++++++++++-------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/resources/js/beatmapsets-show/controller.ts b/resources/js/beatmapsets-show/controller.ts index 15f9acc725d..a48de73ff41 100644 --- a/resources/js/beatmapsets-show/controller.ts +++ b/resources/js/beatmapsets-show/controller.ts @@ -83,27 +83,34 @@ export default class Controller { } @computed - get tags() { - const tagMap = new Map(); + get relatedTags() { + const map = new Map(); for (const tag of this.beatmapset.related_tags) { - tagMap.set(tag.id, asTagJsonWithCount(tag)); + map.set(tag.id, tag); } - for (const beatmap of this.beatmapset.beatmaps) { - if (beatmap.top_tag_ids == null) continue; + return map; + } + + @computed + get tags() { + const userTags: TagJsonWithCount[] = []; - for (const tagId of beatmap.top_tag_ids) { - const tag = tagMap.get(tagId.tag_id); - if (tag == null) continue; + if (this.currentBeatmap.top_tag_ids != null) { + for (const tagId of this.currentBeatmap.top_tag_ids) { + const maybeTag = this.relatedTags.get(tagId.tag_id); + if (maybeTag == null) continue; - tag.count += tagId.count; + const tag = asTagJsonWithCount(maybeTag); + tag.count = tagId.count; + userTags.push(asTagJsonWithCount(tag)); } } return { mapperTags: this.beatmapset.tags.split(' ').filter(present), - userTags: [...tagMap.values()].sort((a, b) => { + userTags: userTags.sort((a, b) => { const diff = b.count - a.count; return diff !== 0 ? diff : a.name.localeCompare(b.name); }), From f689c12610c0f8722307e812c2eaeb765c5e86ad Mon Sep 17 00:00:00 2001 From: bakaneko Date: Thu, 26 Dec 2024 08:07:45 +0900 Subject: [PATCH 030/153] exclude special groups from being set as beatmap owner (and being gifted supporter tags) --- app/Http/Controllers/Users/LookupController.php | 3 +-- app/Libraries/Beatmapset/ChangeBeatmapOwners.php | 2 +- app/Models/User.php | 9 +++++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Users/LookupController.php b/app/Http/Controllers/Users/LookupController.php index afe871f58ef..725373624c2 100644 --- a/app/Http/Controllers/Users/LookupController.php +++ b/app/Http/Controllers/Users/LookupController.php @@ -35,8 +35,7 @@ public function index() } $users = User::where(fn ($q) => $q->whereIn('user_id', $numericIds)->orWhereIn('username', $stringIds)) - ->where('group_id', '<>', app('groups')->byIdentifier('no_profile')->getKey()) - ->default() + ->defaultForLookup() ->with(UserCompactTransformer::CARD_INCLUDES_PRELOAD) ->get(); diff --git a/app/Libraries/Beatmapset/ChangeBeatmapOwners.php b/app/Libraries/Beatmapset/ChangeBeatmapOwners.php index 87a869871ba..5b137b9a60d 100644 --- a/app/Libraries/Beatmapset/ChangeBeatmapOwners.php +++ b/app/Libraries/Beatmapset/ChangeBeatmapOwners.php @@ -43,7 +43,7 @@ public function handle(): void $newUserIds = $this->userIds->diff($currentOwners); - if (User::whereIn('user_id', $newUserIds->toArray())->default()->count() !== $newUserIds->count()) { + if (User::whereIn('user_id', $newUserIds->toArray())->defaultForLookup()->count() !== $newUserIds->count()) { throw new InvariantException('invalid user_id'); } diff --git a/app/Models/User.php b/app/Models/User.php index dcee75bbedc..7c951737fad 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2032,6 +2032,15 @@ public function scopeDefault($query) ]); } + public function scopeDefaultForLookup(Builder $query): Builder + { + $groups = app('groups'); + + return $query + ->whereNotIn('group_id', [$groups->byIdentifier('no_profile')->getKey(), $groups->byIdentifier('bot')->getKey()]) + ->default(); + } + public function scopeOnline($query) { return $query From dedc1b310201cbb47e0152683d93cb9aef7afb08 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Thu, 26 Dec 2024 18:49:24 +0900 Subject: [PATCH 031/153] add test --- .../Beatmapset/ChangeBeatmapOwnersTest.php | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/Libraries/Beatmapset/ChangeBeatmapOwnersTest.php b/tests/Libraries/Beatmapset/ChangeBeatmapOwnersTest.php index dcafe6da068..df6e815b4e4 100644 --- a/tests/Libraries/Beatmapset/ChangeBeatmapOwnersTest.php +++ b/tests/Libraries/Beatmapset/ChangeBeatmapOwnersTest.php @@ -23,6 +23,14 @@ class ChangeBeatmapOwnersTest extends TestCase { + public static function dataProviderForInvalidUser(): array + { + return [ + ['bot'], + ['no_profile'], + ]; + } + public static function dataProviderForUpdateOwner(): array { return [ @@ -68,6 +76,33 @@ public function testMissingUser(): void Bus::assertDispatched(BeatmapOwnerChange::class); } + /** + * @dataProvider dataProviderForInvalidUser + */ + public function testInvalidUser(string $group): void + { + $moderator = User::factory()->withGroup('nat')->create(); + $owner = User::factory()->create(); + $invalidUser = User::factory()->withGroup($group)->create(); + + $beatmap = Beatmap::factory() + ->for(Beatmapset::factory()->pending()->owner($owner)) + ->owner($owner) + ->create(); + + $this->expectCountChange(fn () => BeatmapsetEvent::count(), 0); + + $this->expectExceptionCallable( + fn () => (new ChangeBeatmapOwners($beatmap, [$invalidUser->getKey()], $moderator))->handle(), + InvariantException::class, + ); + + $beatmap = $beatmap->fresh(); + $this->assertEqualsCanonicalizing([$owner->getKey()], $beatmap->getOwners()->pluck('user_id')->toArray()); + + Bus::assertNotDispatched(BeatmapOwnerChange::class); + } + /** * @dataProvider dataProviderForUpdateOwner */ From 913b824b3e2239201fc72ae179ed5b207e52bcc0 Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Thu, 26 Dec 2024 22:15:41 +0100 Subject: [PATCH 032/153] add season user total score calculation workflow --- .env.example | 2 + .../Commands/UserSeasonScoresRecalculate.php | 59 ++++++++++++++ app/Models/Multiplayer/Room.php | 14 ++++ app/Models/Season.php | 27 +++++++ app/Models/SeasonScoreFactor.php | 16 ++++ app/Models/User.php | 5 ++ app/Models/UserSeasonScore.php | 78 +++++++++++++++++++ config/osu.php | 8 +- ...190453_create_user_season_scores_table.php | 34 ++++++++ ...24_10_22_194840_add_parent_id_to_rooms.php | 31 ++++++++ ...3228_create_season_score_factors_table.php | 31 ++++++++ resources/lang/en/rankings.php | 3 + 12 files changed, 303 insertions(+), 5 deletions(-) create mode 100644 app/Console/Commands/UserSeasonScoresRecalculate.php create mode 100644 app/Models/SeasonScoreFactor.php create mode 100644 app/Models/UserSeasonScore.php create mode 100644 database/migrations/2024_10_22_190453_create_user_season_scores_table.php create mode 100644 database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php create mode 100644 database/migrations/2024_10_23_173228_create_season_score_factors_table.php diff --git a/.env.example b/.env.example index 4875ae3057e..178701fa43b 100644 --- a/.env.example +++ b/.env.example @@ -292,6 +292,8 @@ CLIENT_CHECK_VERSION=false # SCORES_SUBMISSION_ENABLED=1 # SCORE_INDEX_MAX_ID_DISTANCE=10_000_000 +# SEASONS_FACTORS_CACHE_DURATION=60 + # BANCHO_BOT_USER_ID= # OCTANE_LOCAL_CACHE_EXPIRE_SECOND=60 diff --git a/app/Console/Commands/UserSeasonScoresRecalculate.php b/app/Console/Commands/UserSeasonScoresRecalculate.php new file mode 100644 index 00000000000..4aebfcdb783 --- /dev/null +++ b/app/Console/Commands/UserSeasonScoresRecalculate.php @@ -0,0 +1,59 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +namespace App\Console\Commands; + +use App\Models\Multiplayer\UserScoreAggregate; +use App\Models\Season; +use App\Models\User; +use Illuminate\Console\Command; + +class UserSeasonScoresRecalculate extends Command +{ + protected $signature = 'user-season-scores:recalculate {--season-id=}'; + protected $description = 'Recalculate user scores for all active seasons or a specified season.'; + + public function handle() + { + $seasonId = $this->option('season-id'); + + if (present($seasonId)) { + $this->recalculate(Season::findOrFail(get_int($seasonId))); + } else { + $activeSeasons = Season::active()->get(); + + foreach ($activeSeasons as $season) { + $this->recalculate($season); + } + } + } + + protected function recalculate(Season $season) { + $scoreUserIds = UserScoreAggregate::whereIn('room_id', $season->rooms->pluck('id')) + ->select('user_id') + ->get() + ->pluck('user_id') + ->unique(); + + $bar = $this->output->createProgressBar($scoreUserIds->count()); + + User::whereIn('user_id', $scoreUserIds) + ->chunkById(100, function ($userChunk) use ($bar, $season) { + foreach ($userChunk as $user) { + $seasonScore = $user->seasonScores() + ->where('season_id', $season->getKey()) + ->firstOrNew(); + + $seasonScore->season_id = $season->getKey(); + $seasonScore->calculate(); + $seasonScore->save(); + + $bar->advance(); + } + }); + + $bar->finish(); + } +} diff --git a/app/Models/Multiplayer/Room.php b/app/Models/Multiplayer/Room.php index c50ed5d63e3..d85b3e9b596 100644 --- a/app/Models/Multiplayer/Room.php +++ b/app/Models/Multiplayer/Room.php @@ -37,6 +37,7 @@ * @property int $id * @property int|null $max_attempts * @property string $name + * @property int|null $parent_id * @property int $participant_count * @property \Illuminate\Database\Eloquent\Collection $playlist PlaylistItem * @property \Illuminate\Database\Eloquent\Collection $scoreLinks ScoreLink @@ -446,6 +447,19 @@ public function completePlay(ScoreToken $scoreToken, array $params): ScoreLink $stats->save(); } + // spotlight playlists should always be linked to one season exactly + if ($this->category === 'spotlight' && $this->seasons()->count() === 1 && $agg->total_score > 0) { + $seasonId = $this->seasons()->first()->getKey(); + + $seasonScore = $user->seasonScores() + ->where('season_id', $seasonId) + ->firstOrNew(); + + $seasonScore->season_id = $seasonId; + $seasonScore->calculate(); + $seasonScore->save(); + } + return $scoreLink; }); } diff --git a/app/Models/Season.php b/app/Models/Season.php index f805a4f3e38..52e04f368ac 100644 --- a/app/Models/Season.php +++ b/app/Models/Season.php @@ -10,6 +10,7 @@ use Carbon\Carbon; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Database\Eloquent\Relations\HasMany; /** * @property bool $finalised @@ -23,6 +24,11 @@ class Season extends Model 'finalised' => 'boolean', ]; + public function scopeActive($query) + { + return $query->where('finalised', false); + } + public static function latestOrId($id) { if ($id === 'latest') { @@ -45,6 +51,27 @@ public function endDate(): ?Carbon : null; } + public function scoreFactors(): HasMany + { + return $this->hasMany(SeasonScoreFactor::class); + } + + public function scoreFactorsOrderedForCalculation(): array + { + return cache_remember_mutexed( + "score_factors:{$this->id}", + $GLOBALS['cfg']['osu']['seasons']['factors_cache_duration'], + [], + function () { + return $this->scoreFactors() + ->orderByDesc('factor') + ->get() + ->pluck('factor') + ->toArray(); + } + ); + } + public function startDate(): ?Carbon { return $this->rooms->min('starts_at'); diff --git a/app/Models/SeasonScoreFactor.php b/app/Models/SeasonScoreFactor.php new file mode 100644 index 00000000000..0e658b5aed3 --- /dev/null +++ b/app/Models/SeasonScoreFactor.php @@ -0,0 +1,16 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +namespace App\Models; + +/** + * @property int $id + * @property float $factor + * @property int $season_id + */ +class SeasonScoreFactor extends Model +{ + public $timestamps = false; +} diff --git a/app/Models/User.php b/app/Models/User.php index dcee75bbedc..d7dbe0f1aae 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -1359,6 +1359,11 @@ public function country() return $this->belongsTo(Country::class, 'country_acronym'); } + public function seasonScores() + { + return $this->hasMany(UserSeasonScore::class); + } + public function statisticsOsu() { return $this->hasOne(UserStatistics\Osu::class); diff --git a/app/Models/UserSeasonScore.php b/app/Models/UserSeasonScore.php new file mode 100644 index 00000000000..865a1b13097 --- /dev/null +++ b/app/Models/UserSeasonScore.php @@ -0,0 +1,78 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +namespace App\Models; + +use App\Exceptions\InvariantException; +use App\Models\Multiplayer\UserScoreAggregate; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Support\Facades\DB; + +/** + * @property int $season_id + * @property int $total_score + * @property int $user_id + */ +class UserSeasonScore extends Model +{ + public $timestamps = false; + public $incrementing = false; + + protected $primaryKey = ':composite'; + protected $primaryKeys = ['user_id', 'season_id']; + + + public function calculate(): void + { + $userScores = UserScoreAggregate::whereIn('room_id', $this->season->rooms->pluck('id')) + ->where('user_id', $this->user_id) + ->get(); + + $scores = []; + + foreach ($this->season->rooms->where('parent_id', null) as $room) { + $totalScore = $userScores->where('room_id', $room->getKey()) + ->first() + ?->total_score; + + $childRoomId = $this->season->rooms + ->where('parent_id', $room->getKey()) + ->first() + ?->getKey(); + + $totalScoreChild = $userScores->where('room_id', $childRoomId) + ->first() + ?->total_score; + + if ($totalScore == null && $totalScoreChild == null) { + continue; + } + + $scores[] = max([$totalScore, $totalScoreChild]); + } + + rsort($scores); + + $factors = $this->season->scoreFactorsOrderedForCalculation(); + $scoreCount = count($scores); + + if ($scoreCount > count($factors)) { + throw new InvariantException(osu_trans('rankings.seasons.validation.not_enough_factors')); + } + + $total = 0; + + for ($i = 0; $i < $scoreCount; $i++) { + $total += $scores[$i] * $factors[$i]; + } + + $this->total_score = $total; + } + + public function season(): BelongsTo + { + return $this->belongsTo(Season::class); + } +} diff --git a/config/osu.php b/config/osu.php index de037d65fbd..7e24153124a 100644 --- a/config/osu.php +++ b/config/osu.php @@ -18,7 +18,6 @@ 'achievement' => [ 'icon_prefix' => env('USER_ACHIEVEMENT_ICON_PREFIX', 'https://assets.ppy.sh/user-achievements/'), ], - 'api' => [ // changing the throttle rate doesn't reset any existing timers, // changing the prefix key is the only way to invalidate them. @@ -27,7 +26,6 @@ 'scores_download' => env('API_THROTTLE_SCORES_DOWNLOAD', '10,1,api-scores-download'), ], ], - 'avatar' => [ 'cache_purge_prefix' => env('AVATAR_CACHE_PURGE_PREFIX'), 'cache_purge_method' => env('AVATAR_CACHE_PURGE_METHOD'), @@ -35,7 +33,6 @@ 'default' => env('DEFAULT_AVATAR', env('APP_URL', 'http://localhost').'/images/layout/avatar-guest@2x.png'), 'storage' => env('AVATAR_STORAGE', 'local-avatar'), ], - 'bbcode' => [ // this should be random or a config variable. // ...who am I kidding, this shouldn't even exist at all. @@ -193,12 +190,13 @@ 'processing_queue' => presence(env('SCORES_PROCESSING_QUEUE')) ?? 'osu-queue:score-statistics', 'submission_enabled' => get_bool(env('SCORES_SUBMISSION_ENABLED')) ?? true, ], - 'seasonal' => [ 'contest_id' => get_int(env('SEASONAL_CONTEST_ID')), 'ends_at' => env('SEASONAL_ENDS_AT'), ], - + 'seasons' => [ + 'factors_cache_duration' => 60 * (get_float(env('SEASONS_FACTORS_CACHE_DURATION')) ?? 60), // in minutes, converted to seconds + ], 'store' => [ 'notice' => presence(str_replace('\n', "\n", env('STORE_NOTICE') ?? '')), ], diff --git a/database/migrations/2024_10_22_190453_create_user_season_scores_table.php b/database/migrations/2024_10_22_190453_create_user_season_scores_table.php new file mode 100644 index 00000000000..a483dfdee3e --- /dev/null +++ b/database/migrations/2024_10_22_190453_create_user_season_scores_table.php @@ -0,0 +1,34 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; + +return new class extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('user_season_scores', function (Blueprint $table) { + $table->bigInteger('user_id')->unsigned(); + $table->integer('season_id')->unsigned(); + $table->integer('total_score'); + + $table->primary(['user_id', 'season_id']); + $table->index('total_score'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_season_scores'); + } +}; diff --git a/database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php b/database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php new file mode 100644 index 00000000000..80055d0c4a8 --- /dev/null +++ b/database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php @@ -0,0 +1,31 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; + +return new class extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::table('multiplayer_rooms', function (Blueprint $table) { + $table->bigInteger('parent_id')->nullable()->unique(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('multiplayer_rooms', function (Blueprint $table) { + $table->dropColumn('parent_id'); + }); + } +}; diff --git a/database/migrations/2024_10_23_173228_create_season_score_factors_table.php b/database/migrations/2024_10_23_173228_create_season_score_factors_table.php new file mode 100644 index 00000000000..699ab950b29 --- /dev/null +++ b/database/migrations/2024_10_23_173228_create_season_score_factors_table.php @@ -0,0 +1,31 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; + +return new class extends Migration +{ + /** + * Run the migrations. + */ + public function up(): void + { + Schema::create('season_score_factors', function (Blueprint $table) { + $table->id(); + $table->integer('season_id'); + $table->float('factor'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('season_score_factors'); + } +}; diff --git a/resources/lang/en/rankings.php b/resources/lang/en/rankings.php index e1dc187c861..cdafa9bfd26 100644 --- a/resources/lang/en/rankings.php +++ b/resources/lang/en/rankings.php @@ -49,6 +49,9 @@ 'ongoing' => 'This season is still ongoing (there will be more playlists added).', 'room_count' => 'Playlist count', 'url' => 'Display more informations on that season.', + 'validation' => [ + 'not_enough_factors' => 'there is not enough score factors for proper calculation', + ], ], 'spotlight' => [ From a7041044f81dab074ea1b1d309ccd8d2d364e5d3 Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Thu, 26 Dec 2024 22:16:53 +0100 Subject: [PATCH 033/153] return type on user relation --- app/Models/User.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/User.php b/app/Models/User.php index d7dbe0f1aae..fe8e03b33e9 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -1359,7 +1359,7 @@ public function country() return $this->belongsTo(Country::class, 'country_acronym'); } - public function seasonScores() + public function seasonScores(): HasMany { return $this->hasMany(UserSeasonScore::class); } From 398ad6dab7534370a2ff1517e828556d3ad1c654 Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Thu, 26 Dec 2024 22:20:48 +0100 Subject: [PATCH 034/153] check total score before querying season count --- app/Models/Multiplayer/Room.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Multiplayer/Room.php b/app/Models/Multiplayer/Room.php index d85b3e9b596..1009f6326ea 100644 --- a/app/Models/Multiplayer/Room.php +++ b/app/Models/Multiplayer/Room.php @@ -448,7 +448,7 @@ public function completePlay(ScoreToken $scoreToken, array $params): ScoreLink } // spotlight playlists should always be linked to one season exactly - if ($this->category === 'spotlight' && $this->seasons()->count() === 1 && $agg->total_score > 0) { + if ($this->category === 'spotlight' && $agg->total_score > 0 && $this->seasons()->count() === 1) { $seasonId = $this->seasons()->first()->getKey(); $seasonScore = $user->seasonScores() From 1cc8f4267c7c0e7c71e35d1e6212be3f4c800f37 Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Thu, 26 Dec 2024 22:23:38 +0100 Subject: [PATCH 035/153] remove unused import --- app/Models/UserSeasonScore.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/Models/UserSeasonScore.php b/app/Models/UserSeasonScore.php index 865a1b13097..1624f6353fc 100644 --- a/app/Models/UserSeasonScore.php +++ b/app/Models/UserSeasonScore.php @@ -8,7 +8,6 @@ use App\Exceptions\InvariantException; use App\Models\Multiplayer\UserScoreAggregate; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Support\Facades\DB; /** * @property int $season_id @@ -23,7 +22,6 @@ class UserSeasonScore extends Model protected $primaryKey = ':composite'; protected $primaryKeys = ['user_id', 'season_id']; - public function calculate(): void { $userScores = UserScoreAggregate::whereIn('room_id', $this->season->rooms->pluck('id')) From db1de9c6ee7c87beb1ec7311c6b76655e52b1586 Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Thu, 26 Dec 2024 22:28:19 +0100 Subject: [PATCH 036/153] use triple equal sign --- app/Models/UserSeasonScore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/UserSeasonScore.php b/app/Models/UserSeasonScore.php index 1624f6353fc..f0a7b4629e1 100644 --- a/app/Models/UserSeasonScore.php +++ b/app/Models/UserSeasonScore.php @@ -44,7 +44,7 @@ public function calculate(): void ->first() ?->total_score; - if ($totalScore == null && $totalScoreChild == null) { + if ($totalScore === null && $totalScoreChild == null) { continue; } From 77a94df03fb9d110c4bcd2e0303ac148c08c253c Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Thu, 26 Dec 2024 22:30:35 +0100 Subject: [PATCH 037/153] one more equal --- app/Models/UserSeasonScore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/UserSeasonScore.php b/app/Models/UserSeasonScore.php index f0a7b4629e1..74f165e1218 100644 --- a/app/Models/UserSeasonScore.php +++ b/app/Models/UserSeasonScore.php @@ -44,7 +44,7 @@ public function calculate(): void ->first() ?->total_score; - if ($totalScore === null && $totalScoreChild == null) { + if ($totalScore === null && $totalScoreChild === null) { continue; } From cf3bc2461ba834a92fadf4b5e8eb7b5b8d20b297 Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Thu, 26 Dec 2024 22:39:01 +0100 Subject: [PATCH 038/153] newline on function opening brace --- app/Console/Commands/UserSeasonScoresRecalculate.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Console/Commands/UserSeasonScoresRecalculate.php b/app/Console/Commands/UserSeasonScoresRecalculate.php index 4aebfcdb783..6ac5d61e9be 100644 --- a/app/Console/Commands/UserSeasonScoresRecalculate.php +++ b/app/Console/Commands/UserSeasonScoresRecalculate.php @@ -30,7 +30,8 @@ public function handle() } } - protected function recalculate(Season $season) { + protected function recalculate(Season $season) + { $scoreUserIds = UserScoreAggregate::whereIn('room_id', $season->rooms->pluck('id')) ->select('user_id') ->get() From dced08a1ae081bab5efc69ff1c67ced92ce1cefc Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Fri, 27 Dec 2024 19:39:03 +0100 Subject: [PATCH 039/153] use float for total score instead --- app/Models/UserSeasonScore.php | 2 +- .../2024_10_22_190453_create_user_season_scores_table.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Models/UserSeasonScore.php b/app/Models/UserSeasonScore.php index 74f165e1218..382a5862773 100644 --- a/app/Models/UserSeasonScore.php +++ b/app/Models/UserSeasonScore.php @@ -11,7 +11,7 @@ /** * @property int $season_id - * @property int $total_score + * @property float $total_score * @property int $user_id */ class UserSeasonScore extends Model diff --git a/database/migrations/2024_10_22_190453_create_user_season_scores_table.php b/database/migrations/2024_10_22_190453_create_user_season_scores_table.php index a483dfdee3e..9404fe4a539 100644 --- a/database/migrations/2024_10_22_190453_create_user_season_scores_table.php +++ b/database/migrations/2024_10_22_190453_create_user_season_scores_table.php @@ -17,7 +17,7 @@ public function up(): void Schema::create('user_season_scores', function (Blueprint $table) { $table->bigInteger('user_id')->unsigned(); $table->integer('season_id')->unsigned(); - $table->integer('total_score'); + $table->float('total_score'); $table->primary(['user_id', 'season_id']); $table->index('total_score'); From d723a8520041b1a9a6d028824261a5b7623700bd Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Fri, 27 Dec 2024 19:57:41 +0100 Subject: [PATCH 040/153] check factor count before calculation --- app/Models/UserSeasonScore.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/Models/UserSeasonScore.php b/app/Models/UserSeasonScore.php index 382a5862773..8db18613e40 100644 --- a/app/Models/UserSeasonScore.php +++ b/app/Models/UserSeasonScore.php @@ -28,9 +28,16 @@ public function calculate(): void ->where('user_id', $this->user_id) ->get(); + $factors = $this->season->scoreFactorsOrderedForCalculation(); + $parentRooms = $this->season->rooms->where('parent_id', null); + + if ($parentRooms->count() > count($factors)) { + throw new InvariantException(osu_trans('rankings.seasons.validation.not_enough_factors')); + } + $scores = []; - foreach ($this->season->rooms->where('parent_id', null) as $room) { + foreach ($parentRooms as $room) { $totalScore = $userScores->where('room_id', $room->getKey()) ->first() ?->total_score; @@ -53,13 +60,7 @@ public function calculate(): void rsort($scores); - $factors = $this->season->scoreFactorsOrderedForCalculation(); $scoreCount = count($scores); - - if ($scoreCount > count($factors)) { - throw new InvariantException(osu_trans('rankings.seasons.validation.not_enough_factors')); - } - $total = 0; for ($i = 0; $i < $scoreCount; $i++) { From 08b4b3415d746c528b639fa17b5d5ae6359aa089 Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Fri, 27 Dec 2024 22:14:06 +0100 Subject: [PATCH 041/153] add tests --- app/Models/SeasonRoom.php | 3 + app/Models/SeasonScoreFactor.php | 4 + database/factories/SeasonRoomFactory.php | 21 ++ .../factories/SeasonScoreFactorFactory.php | 22 ++ tests/Models/UserSeasonScoreTest.php | 202 ++++++++++++++++++ 5 files changed, 252 insertions(+) create mode 100644 database/factories/SeasonRoomFactory.php create mode 100644 database/factories/SeasonScoreFactorFactory.php create mode 100644 tests/Models/UserSeasonScoreTest.php diff --git a/app/Models/SeasonRoom.php b/app/Models/SeasonRoom.php index 86397032a1d..a5bbfc281c3 100644 --- a/app/Models/SeasonRoom.php +++ b/app/Models/SeasonRoom.php @@ -7,6 +7,7 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; /** @@ -16,5 +17,7 @@ */ class SeasonRoom extends Model { + use HasFactory; + public $timestamps = false; } diff --git a/app/Models/SeasonScoreFactor.php b/app/Models/SeasonScoreFactor.php index 0e658b5aed3..aff70c8fa59 100644 --- a/app/Models/SeasonScoreFactor.php +++ b/app/Models/SeasonScoreFactor.php @@ -5,6 +5,8 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Factories\HasFactory; + /** * @property int $id * @property float $factor @@ -12,5 +14,7 @@ */ class SeasonScoreFactor extends Model { + use HasFactory; + public $timestamps = false; } diff --git a/database/factories/SeasonRoomFactory.php b/database/factories/SeasonRoomFactory.php new file mode 100644 index 00000000000..3492702f4f3 --- /dev/null +++ b/database/factories/SeasonRoomFactory.php @@ -0,0 +1,21 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +namespace Database\Factories; + +use App\Models\SeasonRoom; + +class SeasonRoomFactory extends Factory +{ + protected $model = SeasonRoom::class; + + public function definition(): array + { + // pivot table... + return []; + } +} diff --git a/database/factories/SeasonScoreFactorFactory.php b/database/factories/SeasonScoreFactorFactory.php new file mode 100644 index 00000000000..1df687bdf53 --- /dev/null +++ b/database/factories/SeasonScoreFactorFactory.php @@ -0,0 +1,22 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +namespace Database\Factories; + +use App\Models\SeasonScoreFactor; + +class SeasonScoreFactorFactory extends Factory +{ + protected $model = SeasonScoreFactor::class; + + public function definition(): array + { + return [ + 'factor' => 1, + ]; + } +} diff --git a/tests/Models/UserSeasonScoreTest.php b/tests/Models/UserSeasonScoreTest.php new file mode 100644 index 00000000000..11e5f9d9eab --- /dev/null +++ b/tests/Models/UserSeasonScoreTest.php @@ -0,0 +1,202 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +namespace Tests\Models; + +use App\Models\Multiplayer\PlaylistItem; +use App\Models\Multiplayer\Room; +use App\Models\Season; +use App\Models\SeasonRoom; +use App\Models\SeasonScoreFactor; +use App\Models\User; +use App\Models\UserSeasonScore; +use Tests\TestCase; + +class UserSeasonScoreTest extends TestCase +{ + private Season $season; + private User $user; + + public function testAddMultipleScores(): void + { + foreach ([1, 0.75, 0.5] as $factor) { + SeasonScoreFactor::factory()->create([ + 'factor' => $factor, + 'season_id' => $this->season, + ]); + } + + $this->createRoomWithPlay(10); + + $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) + ->where('season_id', $this->season->getKey()) + ->first(); + + $this->assertSame($userScore->total_score, (float) 10); // 10*1 + + $this->createRoomWithPlay(15); + + $userScore->refresh(); + $this->assertSame($userScore->total_score, 22.5); // 15*1 + 10*0.75 + + $this->createRoomWithPlay(25); + + $userScore->refresh(); + $this->assertSame($userScore->total_score, 41.25); // 25*1 + 15*0.75 + 10*0.5 + } + + public function testAddMultipleScoresWithChildrenRooms(): void + { + foreach ([1, 0.75, 0.5] as $factor) { + SeasonScoreFactor::factory()->create([ + 'factor' => $factor, + 'season_id' => $this->season, + ]); + } + + $firstRoom = $this->createRoomWithPlay(10); + + $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) + ->where('season_id', $this->season->getKey()) + ->first(); + + $this->assertSame($userScore->total_score, (float) 10); // 10*1 + + $this->createRoomWithPlay(15, $firstRoom->getKey()); + + $userScore->refresh(); + $this->assertSame($userScore->total_score, (float) 15); // 15*1 + + $secondRoom = $this->createRoomWithPlay(20); + + $userScore->refresh(); + $this->assertSame($userScore->total_score, 31.25); // 20*1 + 15*0.75 + + $this->createRoomWithPlay(20, $secondRoom->getKey()); + + $userScore->refresh(); + $this->assertSame($userScore->total_score, 31.25); // 20*1 + 15*0.75 + + $thirdRoom = $this->createRoomWithPlay(10); + + $userScore->refresh(); + $this->assertSame($userScore->total_score, 36.25); // 20*1 + 15*0.75 + 10*0.5 + + $this->createRoomWithPlay(30, $thirdRoom->getKey()); + + $userScore->refresh(); + $this->assertSame($userScore->total_score, 52.5); // 30*1 + 20*0.75 + 15*0.5 + } + + public function testAddHigherScoreInChildRoom(): void + { + SeasonScoreFactor::factory()->create(['season_id' => $this->season]); + + $room = $this->createRoomWithPlay(10); + + $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) + ->where('season_id', $this->season->getKey()) + ->first(); + + $this->assertSame($userScore->total_score, (float) 10); + + $this->createRoomWithPlay(15, $room->getKey()); + + $userScore->refresh(); + $this->assertSame($userScore->total_score, (float) 15); + } + + public function testAddHigherScoreInParentRoom(): void + { + SeasonScoreFactor::factory()->create(['season_id' => $this->season]); + + $room = $this->createRoomWithPlay(15); + + $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) + ->where('season_id', $this->season->getKey()) + ->first(); + + $this->assertSame($userScore->total_score, (float) 15); + + $this->createRoomWithPlay(10, $room->getKey()); + + $userScore->refresh(); + $this->assertSame($userScore->total_score, (float) 15); + } + + public function testAddSameScoreInChildAndParentRoom(): void + { + SeasonScoreFactor::factory()->create(['season_id' => $this->season]); + + $room = $this->createRoomWithPlay(10); + + $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) + ->where('season_id', $this->season->getKey()) + ->first(); + + $this->assertSame($userScore->total_score, (float) 10); + + $this->createRoomWithPlay(10, $room->getKey()); + + $userScore->refresh(); + $this->assertSame($userScore->total_score, (float) 10); + } + + public function testAddScoreInChildRoomOnly(): void + { + SeasonScoreFactor::factory()->create(['season_id' => $this->season]); + + $room = $this->createRoom(); + $this->createRoomWithPlay(10, $room->getKey()); + + $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) + ->where('season_id', $this->season->getKey()) + ->first(); + + $this->assertSame($userScore->total_score, (float) 10); + } + + protected function setUp(): void + { + parent::setUp(); + + $this->season = Season::factory()->create(); + $this->user = User::factory()->create(); + } + + private function createRoom(?int $parentId = null): Room + { + $room = Room::factory()->create([ + 'category' => 'spotlight', + 'parent_id' => $parentId, + ]); + + SeasonRoom::factory()->create([ + 'room_id' => $room, + 'season_id' => $this->season, + ]); + + return $room; + } + + private function createRoomWithPlay(float $totalScore, ?int $parentId = null): Room + { + $room = $this->createRoom($parentId); + + $playlistItem = PlaylistItem::factory()->create([ + 'owner_id' => $room->host, + 'room_id' => $room, + ]); + + $this->roomAddPlay($this->user, $playlistItem, [ + 'passed' => true, + 'total_score' => $totalScore, + ]); + + return $room; + } +} From ea6bd9b66b12256ac64521ac7aed3d8efb4bf10b Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Fri, 27 Dec 2024 22:18:35 +0100 Subject: [PATCH 042/153] strict types --- app/Console/Commands/UserSeasonScoresRecalculate.php | 6 ++++-- app/Models/SeasonScoreFactor.php | 2 ++ app/Models/UserSeasonScore.php | 2 ++ .../2024_10_22_190453_create_user_season_scores_table.php | 2 ++ .../migrations/2024_10_22_194840_add_parent_id_to_rooms.php | 2 ++ .../2024_10_23_173228_create_season_score_factors_table.php | 2 ++ 6 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/Console/Commands/UserSeasonScoresRecalculate.php b/app/Console/Commands/UserSeasonScoresRecalculate.php index 6ac5d61e9be..7ed541c50b1 100644 --- a/app/Console/Commands/UserSeasonScoresRecalculate.php +++ b/app/Console/Commands/UserSeasonScoresRecalculate.php @@ -3,6 +3,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. // See the LICENCE file in the repository root for full licence text. +declare(strict_types=1); + namespace App\Console\Commands; use App\Models\Multiplayer\UserScoreAggregate; @@ -15,7 +17,7 @@ class UserSeasonScoresRecalculate extends Command protected $signature = 'user-season-scores:recalculate {--season-id=}'; protected $description = 'Recalculate user scores for all active seasons or a specified season.'; - public function handle() + public function handle(): void { $seasonId = $this->option('season-id'); @@ -30,7 +32,7 @@ public function handle() } } - protected function recalculate(Season $season) + protected function recalculate(Season $season): void { $scoreUserIds = UserScoreAggregate::whereIn('room_id', $season->rooms->pluck('id')) ->select('user_id') diff --git a/app/Models/SeasonScoreFactor.php b/app/Models/SeasonScoreFactor.php index aff70c8fa59..bd270094fa8 100644 --- a/app/Models/SeasonScoreFactor.php +++ b/app/Models/SeasonScoreFactor.php @@ -3,6 +3,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. // See the LICENCE file in the repository root for full licence text. +declare(strict_types=1); + namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; diff --git a/app/Models/UserSeasonScore.php b/app/Models/UserSeasonScore.php index 8db18613e40..6ae74840caf 100644 --- a/app/Models/UserSeasonScore.php +++ b/app/Models/UserSeasonScore.php @@ -3,6 +3,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. // See the LICENCE file in the repository root for full licence text. +declare(strict_types=1); + namespace App\Models; use App\Exceptions\InvariantException; diff --git a/database/migrations/2024_10_22_190453_create_user_season_scores_table.php b/database/migrations/2024_10_22_190453_create_user_season_scores_table.php index 9404fe4a539..cd7045105c5 100644 --- a/database/migrations/2024_10_22_190453_create_user_season_scores_table.php +++ b/database/migrations/2024_10_22_190453_create_user_season_scores_table.php @@ -3,6 +3,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. // See the LICENCE file in the repository root for full licence text. +declare(strict_types=1); + use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; diff --git a/database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php b/database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php index 80055d0c4a8..409b0f77007 100644 --- a/database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php +++ b/database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php @@ -3,6 +3,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. // See the LICENCE file in the repository root for full licence text. +declare(strict_types=1); + use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; diff --git a/database/migrations/2024_10_23_173228_create_season_score_factors_table.php b/database/migrations/2024_10_23_173228_create_season_score_factors_table.php index 699ab950b29..9adca317b84 100644 --- a/database/migrations/2024_10_23_173228_create_season_score_factors_table.php +++ b/database/migrations/2024_10_23_173228_create_season_score_factors_table.php @@ -3,6 +3,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. // See the LICENCE file in the repository root for full licence text. +declare(strict_types=1); + use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; From cb30c70e1e30df589711368ec08c59802c07fe74 Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:19:59 +0100 Subject: [PATCH 043/153] make parent_id unsigned --- .../migrations/2024_10_22_194840_add_parent_id_to_rooms.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php b/database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php index 409b0f77007..29bea618cf6 100644 --- a/database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php +++ b/database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php @@ -17,7 +17,10 @@ public function up(): void { Schema::table('multiplayer_rooms', function (Blueprint $table) { - $table->bigInteger('parent_id')->nullable()->unique(); + $table->bigInteger('parent_id') + ->unsigned() + ->nullable() + ->unique(); }); } From 61ef3dca92852b7aabf205588586e31ac472ddf9 Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:21:10 +0100 Subject: [PATCH 044/153] alphabetize --- app/Models/UserSeasonScore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/UserSeasonScore.php b/app/Models/UserSeasonScore.php index 6ae74840caf..51825ef866c 100644 --- a/app/Models/UserSeasonScore.php +++ b/app/Models/UserSeasonScore.php @@ -18,8 +18,8 @@ */ class UserSeasonScore extends Model { - public $timestamps = false; public $incrementing = false; + public $timestamps = false; protected $primaryKey = ':composite'; protected $primaryKeys = ['user_id', 'season_id']; From 845dcfee38ed908f22f1a682f2e4d3c5c55819f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 Jan 2025 15:22:06 +0900 Subject: [PATCH 045/153] Add `osu_logins` migration For use in https://github.com/ppy/osu-server-spectator/pull/253. --- .../2025_01_06_000000_create_osu_logins.php | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 database/migrations/2025_01_06_000000_create_osu_logins.php diff --git a/database/migrations/2025_01_06_000000_create_osu_logins.php b/database/migrations/2025_01_06_000000_create_osu_logins.php new file mode 100644 index 00000000000..ff42c496bc7 --- /dev/null +++ b/database/migrations/2025_01_06_000000_create_osu_logins.php @@ -0,0 +1,36 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; + +return new class extends Migration +{ + public function up(): void + { + // This is a legacy table. Migration is added for external projects' sake. + if (Schema::hasTable('osu_logins')) { + return; + } + + Schema::create('osu_logins', function (Blueprint $table) { + $table->unsignedInteger('user_id')->default(0); + $table->string('ip', 100)->default(''); + $table->timestamp('date')->useCurrent(); + + $table->index('user_id'); + $table->index('date'); + $table->index('ip'); + }); + } + + public function down(): void + { + Schema::dropIfExists('teams'); + } +}; From 1cae1e27e78e649d122f97e354afe9dd0a9ebcb8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 6 Jan 2025 15:45:03 +0900 Subject: [PATCH 046/153] Add explicit index names and fix `down()` function Co-authored-by: Edho Arief --- .../migrations/2025_01_06_000000_create_osu_logins.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/database/migrations/2025_01_06_000000_create_osu_logins.php b/database/migrations/2025_01_06_000000_create_osu_logins.php index ff42c496bc7..6d0840afa43 100644 --- a/database/migrations/2025_01_06_000000_create_osu_logins.php +++ b/database/migrations/2025_01_06_000000_create_osu_logins.php @@ -23,14 +23,14 @@ public function up(): void $table->string('ip', 100)->default(''); $table->timestamp('date')->useCurrent(); - $table->index('user_id'); - $table->index('date'); - $table->index('ip'); + $table->index('user_id', 'user_id'); + $table->index('date', 'date'); + $table->index('ip', 'ip'); }); } public function down(): void { - Schema::dropIfExists('teams'); + Schema::dropIfExists('osu_logins'); } }; From 00f50652ed2d186b49ab03bce91a27fb6076eae6 Mon Sep 17 00:00:00 2001 From: nanaya Date: Mon, 6 Jan 2025 17:32:34 +0900 Subject: [PATCH 047/153] Delete files first and transact db things --- app/Models/Team.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/app/Models/Team.php b/app/Models/Team.php index 7db9acd9690..33437a2774f 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -78,15 +78,18 @@ public function descriptionHtml(): string public function delete() { - $ret = parent::delete(); + $this->header()->delete(); + $this->logo()->delete(); - if ($ret) { - $this->header()->delete(); - $this->logo()->delete(); - $this->members()->delete(); - } + return $this->getConnection()->transaction(function () { + $ret = parent::delete(); + + if ($ret) { + $this->members()->delete(); + } - return $ret; + return $ret; + }); } public function header(): Uploader From 450750ada3cca25d267b5c5197947f6fba509c2b Mon Sep 17 00:00:00 2001 From: nanaya Date: Mon, 6 Jan 2025 20:28:23 +0900 Subject: [PATCH 048/153] Add test --- tests/Models/TeamTest.php | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/Models/TeamTest.php diff --git a/tests/Models/TeamTest.php b/tests/Models/TeamTest.php new file mode 100644 index 00000000000..55293fe0619 --- /dev/null +++ b/tests/Models/TeamTest.php @@ -0,0 +1,29 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +namespace Tests\Models; + +use App\Models\Team; +use App\Models\TeamMember; +use App\Models\User; +use Tests\TestCase; + +class TeamTest extends TestCase +{ + public function testDelete(): void + { + $team = Team::factory()->create(); + $team->members()->create(['user_id' => User::factory()->create()->getKey()]); + $otherTeam = Team::factory()->create(); + $otherTeam->members()->create(['user_id' => User::factory()->create()->getKey()]); + + $this->expectCountChange(fn () => Team::count(), -1); + $this->expectCountChange(fn () => TeamMember::count(), -2); + + $team->fresh()->delete(); + } +} From 3bc532202a7ae00fbb85bb15a1e86c887a2463ef Mon Sep 17 00:00:00 2001 From: nanaya Date: Mon, 6 Jan 2025 21:31:13 +0900 Subject: [PATCH 049/153] More test --- tests/Models/TeamTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Models/TeamTest.php b/tests/Models/TeamTest.php index 55293fe0619..f15d0ef6999 100644 --- a/tests/Models/TeamTest.php +++ b/tests/Models/TeamTest.php @@ -23,7 +23,10 @@ public function testDelete(): void $this->expectCountChange(fn () => Team::count(), -1); $this->expectCountChange(fn () => TeamMember::count(), -2); + $this->expectCountChange(fn () => $otherTeam->members()->count(), 0); $team->fresh()->delete(); + + $this->assertNotNull($otherTeam->fresh()); } } From 318d76f06c5b4d95d1d13fdefde9b1211569d629 Mon Sep 17 00:00:00 2001 From: nanaya Date: Tue, 7 Jan 2025 15:37:27 +0900 Subject: [PATCH 050/153] Show user card on discussion page user link --- resources/js/beatmap-discussions/user-card.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/resources/js/beatmap-discussions/user-card.tsx b/resources/js/beatmap-discussions/user-card.tsx index 53ab7e3e283..71a34658c74 100644 --- a/resources/js/beatmap-discussions/user-card.tsx +++ b/resources/js/beatmap-discussions/user-card.tsx @@ -3,6 +3,7 @@ import UserAvatar from 'components/user-avatar'; import UserGroupBadge from 'components/user-group-badge'; +import UserLink from 'components/user-link'; import UserGroupJson from 'interfaces/user-group-json'; import UserJson from 'interfaces/user-json'; import { route } from 'laroute'; @@ -32,12 +33,12 @@ export class UserCard extends React.PureComponent { ) : ( - - + )}
@@ -49,12 +50,12 @@ export class UserCard extends React.PureComponent { ) : ( - {this.renderUsername()} - + )} {!this.props.user.is_bot && !this.props.user.is_deleted && ( Date: Tue, 7 Jan 2025 20:10:36 +0900 Subject: [PATCH 051/153] Force browser page refresh on locale change --- app/Http/Controllers/HomeController.php | 2 +- resources/views/layout/ujs_full_reload.blade.php | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 resources/views/layout/ujs_full_reload.blade.php diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 0cd6ff043d9..b1a1b491fd0 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -210,7 +210,7 @@ public function setLocale() ]); } - return ext_view('layout.ujs-reload', [], 'js') + return ext_view('layout.ujs_full_reload', [], 'js') ->withCookie(cookie()->forever('locale', $newLocale)); } diff --git a/resources/views/layout/ujs_full_reload.blade.php b/resources/views/layout/ujs_full_reload.blade.php new file mode 100644 index 00000000000..58a93c38b26 --- /dev/null +++ b/resources/views/layout/ujs_full_reload.blade.php @@ -0,0 +1,5 @@ +{{-- + Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. + See the LICENCE file in the repository root for full licence text. +--}} +window.location.reload(); From 8a72a2a4c7679f6c7a8d608d9990b8902b679f87 Mon Sep 17 00:00:00 2001 From: nanaya Date: Tue, 7 Jan 2025 20:19:21 +0900 Subject: [PATCH 052/153] Reset changelog chart before creating a new one --- .../js/core-legacy/changelog-chart-loader.coffee | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/resources/js/core-legacy/changelog-chart-loader.coffee b/resources/js/core-legacy/changelog-chart-loader.coffee index 2e6a570da3f..75a0e885da7 100644 --- a/resources/js/core-legacy/changelog-chart-loader.coffee +++ b/resources/js/core-legacy/changelog-chart-loader.coffee @@ -10,11 +10,18 @@ export default class ChangelogChartLoader $(window).on 'resize', @resize $(document).on 'turbo:load', @initialize + initialize: => - return if !@container[0]? + container = @container[0] + + return unless container? + + # reset existing chart + container.innerHTML = '' + + container._chart = new ChangelogChart container + container._chart.loadData() - @container[0]._chart = new ChangelogChart @container[0] - @container[0]._chart.loadData() resize: => @container[0]?._chart.resize() From f713a0e736dd0d27abfc76dfd8586371d594315a Mon Sep 17 00:00:00 2001 From: nanaya Date: Tue, 7 Jan 2025 20:22:20 +0900 Subject: [PATCH 053/153] Simplify existence check --- .../js/core-legacy/changelog-chart-loader.coffee | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/resources/js/core-legacy/changelog-chart-loader.coffee b/resources/js/core-legacy/changelog-chart-loader.coffee index 75a0e885da7..884ca05d09d 100644 --- a/resources/js/core-legacy/changelog-chart-loader.coffee +++ b/resources/js/core-legacy/changelog-chart-loader.coffee @@ -4,24 +4,22 @@ import ChangelogChart from 'charts/changelog-chart' export default class ChangelogChartLoader - container: document.getElementsByClassName('js-changelog-chart') - constructor: -> $(window).on 'resize', @resize $(document).on 'turbo:load', @initialize initialize: => - container = @container[0] + @container = document.querySelector('.js-changelog-chart') - return unless container? + return unless @container? # reset existing chart - container.innerHTML = '' + @container.innerHTML = '' - container._chart = new ChangelogChart container - container._chart.loadData() + @container._chart = new ChangelogChart @container + @container._chart.loadData() resize: => - @container[0]?._chart.resize() + @container?._chart.resize() From 8347eb5a18491b37c5a5da69d8496b369044d2b5 Mon Sep 17 00:00:00 2001 From: nanaya Date: Tue, 7 Jan 2025 20:27:58 +0900 Subject: [PATCH 054/153] More precise chart event handling --- .../core-legacy/changelog-chart-loader.coffee | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/resources/js/core-legacy/changelog-chart-loader.coffee b/resources/js/core-legacy/changelog-chart-loader.coffee index 884ca05d09d..25c056497aa 100644 --- a/resources/js/core-legacy/changelog-chart-loader.coffee +++ b/resources/js/core-legacy/changelog-chart-loader.coffee @@ -5,21 +5,27 @@ import ChangelogChart from 'charts/changelog-chart' export default class ChangelogChartLoader constructor: -> - $(window).on 'resize', @resize - $(document).on 'turbo:load', @initialize + document.addEventListener 'turbo:load', @initialize + document.addEventListener 'turbo:before-cache', @reset initialize: => - @container = document.querySelector('.js-changelog-chart') + container = document.querySelector('.js-changelog-chart') - return unless @container? + return unless container? # reset existing chart - @container.innerHTML = '' + container.innerHTML = '' - @container._chart = new ChangelogChart @container - @container._chart.loadData() + @chart = new ChangelogChart container + @chart.loadData() + window.addEventListener 'resize', @resize + + + reset: => + @chart = null + window.removeEventListener 'resize', @resize resize: => - @container?._chart.resize() + @chart.resize() From fa578457f300a9cddfbadd5e85131542ebd0b3e0 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 8 Jan 2025 15:37:27 +0900 Subject: [PATCH 055/153] Modify to boolean 'freestyle' parameter instead --- .../Multiplayer/Rooms/Playlist/ScoresController.php | 13 +++++++------ app/Models/Multiplayer/PlaylistItem.php | 4 ++-- .../Multiplayer/PlaylistItemTransformer.php | 2 +- ...add_freestyle_to_multiplayer_playlist_items.php} | 4 ++-- 4 files changed, 12 insertions(+), 11 deletions(-) rename database/migrations/{2024_12_10_035240_add_beatmapset_id_to_multiplayer_playlist_items.php => 2024_12_10_035240_add_freestyle_to_multiplayer_playlist_items.php} (79%) diff --git a/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php b/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php index 858305be77f..daf52f82020 100644 --- a/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php +++ b/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php @@ -185,15 +185,16 @@ public function store($roomId, $playlistId) $request = \Request::instance(); $params = $request->all(); - if ($playlistItem->beatmapset_id === null) { + if ($playlistItem->freestyle) { + // Todo: Ensure beatmap_hash matches any beatmap from the playlist item's beatmap set. + // Todo: Ensure ruleset_id is valid (converts only allowed for id=0 otherwise must match beatmap playmode, and value within 0..3). + // Todo: Modifying the playlist item looks dodgy to me :)! + $playlistItem->beatmap_id = Beatmap::firstWhere('checksum', get_string($params['beatmap_hash'] ?? null))->beatmap_id; + $playlistItem->ruleset_id = get_int($params['ruleset_id'] ?? null); + } else { if (get_string($params['beatmap_hash'] ?? null) !== $playlistItem->beatmap->checksum) { throw new InvariantException(osu_trans('score_tokens.create.beatmap_hash_invalid')); } - } else { - // Todo: Validate beatmap_hash param matches any checksum from the playlist item's beatmap set. - // Todo: Modifying the playlist item looks dodgy to me and is likely failing some internal validations. - $playlistItem->beatmap_id = Beatmap::firstWhere('checksum', get_string($params['beatmap_hash'] ?? null))->beatmap_id; - $playlistItem->ruleset_id = get_int($params['ruleset_id'] ?? null); } $buildId = ClientCheck::parseToken($request)['buildId']; diff --git a/app/Models/Multiplayer/PlaylistItem.php b/app/Models/Multiplayer/PlaylistItem.php index 73d70ae77ba..b8ab7d8b56b 100644 --- a/app/Models/Multiplayer/PlaylistItem.php +++ b/app/Models/Multiplayer/PlaylistItem.php @@ -16,12 +16,12 @@ * @property json|null $allowed_mods * @property Beatmap $beatmap * @property int $beatmap_id - * @property int|null $beatmapset_id * @property \Carbon\Carbon|null $created_at * @property int $id * @property int $owner_id * @property int|null $playlist_order * @property json|null $required_mods + * @property bool $freestyle * @property Room $room * @property int $room_id * @property int|null $ruleset_id @@ -65,7 +65,7 @@ public static function fromJsonParams(User $owner, $json) $obj->$field = $value; } - $obj->beatmapset_id = get_int($json['beatmapset_id'] ?? null); + $obj->freestyle = get_bool($json['freestyle'] ?? false); $obj->max_attempts = get_int($json['max_attempts'] ?? null); $modsHelper = app('mods'); diff --git a/app/Transformers/Multiplayer/PlaylistItemTransformer.php b/app/Transformers/Multiplayer/PlaylistItemTransformer.php index 7caf2ab77ab..f7b331b05e9 100644 --- a/app/Transformers/Multiplayer/PlaylistItemTransformer.php +++ b/app/Transformers/Multiplayer/PlaylistItemTransformer.php @@ -21,10 +21,10 @@ public function transform(PlaylistItem $item) 'id' => $item->id, 'room_id' => $item->room_id, 'beatmap_id' => $item->beatmap_id, - 'beatmapset_id' => $item->beatmapset_id, 'ruleset_id' => $item->ruleset_id, 'allowed_mods' => $item->allowed_mods, 'required_mods' => $item->required_mods, + 'freestyle' => $item->freestyle, 'expired' => $item->expired, 'owner_id' => $item->owner_id, 'playlist_order' => $item->playlist_order, diff --git a/database/migrations/2024_12_10_035240_add_beatmapset_id_to_multiplayer_playlist_items.php b/database/migrations/2024_12_10_035240_add_freestyle_to_multiplayer_playlist_items.php similarity index 79% rename from database/migrations/2024_12_10_035240_add_beatmapset_id_to_multiplayer_playlist_items.php rename to database/migrations/2024_12_10_035240_add_freestyle_to_multiplayer_playlist_items.php index 70106407a55..d8c3d6bd5c9 100644 --- a/database/migrations/2024_12_10_035240_add_beatmapset_id_to_multiplayer_playlist_items.php +++ b/database/migrations/2024_12_10_035240_add_freestyle_to_multiplayer_playlist_items.php @@ -12,7 +12,7 @@ public function up(): void { Schema::table('multiplayer_playlist_items', function (Blueprint $table) { - $table->unsignedMediumInteger('beatmapset_id')->nullable()->after('beatmap_id'); + $table->boolean('freestyle')->after('required_mods')->default(false); }); } @@ -23,7 +23,7 @@ public function up(): void public function down(): void { Schema::table('multiplayer_playlist_items', function (Blueprint $table) { - $table->dropColumn('beatmapset_id'); + $table->dropColumn('freestyle'); }); } }; From 4699f50af05ab3908e9f33022eee0a6dc9e85ed9 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 8 Jan 2025 19:01:00 +0900 Subject: [PATCH 056/153] Require strict types + add license header --- ...10_035240_add_freestyle_to_multiplayer_playlist_items.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/database/migrations/2024_12_10_035240_add_freestyle_to_multiplayer_playlist_items.php b/database/migrations/2024_12_10_035240_add_freestyle_to_multiplayer_playlist_items.php index d8c3d6bd5c9..50b6076cbab 100644 --- a/database/migrations/2024_12_10_035240_add_freestyle_to_multiplayer_playlist_items.php +++ b/database/migrations/2024_12_10_035240_add_freestyle_to_multiplayer_playlist_items.php @@ -1,5 +1,10 @@ . Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; From 2ecb1f04665aa95737c66895e943a41471384727 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 8 Jan 2025 19:14:12 +0900 Subject: [PATCH 057/153] Fix inspection --- ..._12_10_035240_add_freestyle_to_multiplayer_playlist_items.php | 1 - 1 file changed, 1 deletion(-) diff --git a/database/migrations/2024_12_10_035240_add_freestyle_to_multiplayer_playlist_items.php b/database/migrations/2024_12_10_035240_add_freestyle_to_multiplayer_playlist_items.php index 50b6076cbab..9136afe63b2 100644 --- a/database/migrations/2024_12_10_035240_add_freestyle_to_multiplayer_playlist_items.php +++ b/database/migrations/2024_12_10_035240_add_freestyle_to_multiplayer_playlist_items.php @@ -19,7 +19,6 @@ public function up(): void Schema::table('multiplayer_playlist_items', function (Blueprint $table) { $table->boolean('freestyle')->after('required_mods')->default(false); }); - } /** From cf50e17f6902c2c424b4cc8861e7b8f2848827e8 Mon Sep 17 00:00:00 2001 From: nanaya Date: Wed, 8 Jan 2025 21:15:00 +0900 Subject: [PATCH 058/153] Rework score token validation Mainly in relation to multiplayer play. --- .../Rooms/Playlist/ScoresController.php | 8 +---- .../Controllers/ScoreTokensController.php | 35 +++++++------------ app/Models/Multiplayer/Room.php | 5 +-- app/Models/ScoreToken.php | 33 +++++++++++++++++ database/factories/ScoreTokenFactory.php | 3 +- .../Rooms/Playlist/ScoresControllerTest.php | 2 +- .../Controllers/ScoreTokensControllerTest.php | 22 ++++-------- tests/Models/Multiplayer/RoomTest.php | 16 ++++----- .../Multiplayer/UserScoreAggregateTest.php | 2 +- tests/TestCase.php | 13 ++++++- 10 files changed, 80 insertions(+), 59 deletions(-) diff --git a/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php b/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php index ea1f7321d1f..58ea39c74df 100644 --- a/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php +++ b/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php @@ -5,7 +5,6 @@ namespace App\Http\Controllers\Multiplayer\Rooms\Playlist; -use App\Exceptions\InvariantException; use App\Http\Controllers\Controller as BaseController; use App\Libraries\ClientCheck; use App\Models\Multiplayer\PlaylistItem; @@ -182,15 +181,10 @@ public function store($roomId, $playlistId) $playlistItem = $room->playlist()->findOrFail($playlistId); $user = \Auth::user(); $request = \Request::instance(); - $params = $request->all(); - - if (get_string($params['beatmap_hash'] ?? null) !== $playlistItem->beatmap->checksum) { - throw new InvariantException(osu_trans('score_tokens.create.beatmap_hash_invalid')); - } $buildId = ClientCheck::parseToken($request)['buildId']; - $scoreToken = $room->startPlay($user, $playlistItem, $buildId); + $scoreToken = $room->startPlay($user, $playlistItem, $buildId, $request->all()); return json_item($scoreToken, new ScoreTokenTransformer()); } diff --git a/app/Http/Controllers/ScoreTokensController.php b/app/Http/Controllers/ScoreTokensController.php index 06a392e7358..b135d246d3d 100644 --- a/app/Http/Controllers/ScoreTokensController.php +++ b/app/Http/Controllers/ScoreTokensController.php @@ -29,33 +29,22 @@ public function store($beatmapId) $beatmap = Beatmap::increasesStatistics()->findOrFail($beatmapId); $user = auth()->user(); $request = \Request::instance(); - $params = get_params($request->all(), null, [ - 'beatmap_hash', - 'ruleset_id:int', - ]); - - $checks = [ - 'beatmap_hash' => fn (string $value): bool => $value === $beatmap->checksum, - 'ruleset_id' => fn (int $value): bool => Beatmap::modeStr($value) !== null && $beatmap->canBeConvertedTo($value), - ]; - foreach ($checks as $key => $testFn) { - if (!isset($params[$key])) { - throw new InvariantException("missing {$key}"); - } - if (!$testFn($params[$key])) { - throw new InvariantException("invalid {$key}"); - } - } $buildId = ClientCheck::parseToken($request)['buildId']; + $scoreToken = new ScoreToken([ + 'beatmap_id' => $beatmap->getKey(), + 'build_id' => $buildId, + 'user_id' => $user->getKey(), + ...get_params($request->all(), null, [ + 'beatmap_hash', + 'ruleset_id:int', + ]), + ]); + $scoreToken->setRelation('beatmap', $beatmap); + try { - $scoreToken = ScoreToken::create([ - 'beatmap_id' => $beatmap->getKey(), - 'build_id' => $buildId, - 'ruleset_id' => $params['ruleset_id'], - 'user_id' => $user->getKey(), - ]); + $scoreToken->saveOrExplode(); } catch (PDOException $e) { // TODO: move this to be a validation inside Score model throw new InvariantException('failed creating score token'); diff --git a/app/Models/Multiplayer/Room.php b/app/Models/Multiplayer/Room.php index c50ed5d63e3..2f2abf7e3c1 100644 --- a/app/Models/Multiplayer/Room.php +++ b/app/Models/Multiplayer/Room.php @@ -658,13 +658,13 @@ public function endGame(User $requestingUser) $this->save(); } - public function startPlay(User $user, PlaylistItem $playlistItem, int $buildId) + public function startPlay(User $user, PlaylistItem $playlistItem, int $buildId, array $rawParams): ScoreToken { priv_check_user($user, 'MultiplayerScoreSubmit', $this)->ensureCan(); $this->assertValidStartPlay($user, $playlistItem); - return $this->getConnection()->transaction(function () use ($buildId, $user, $playlistItem) { + return $this->getConnection()->transaction(function () use ($buildId, $playlistItem, $rawParams, $user) { $agg = UserScoreAggregate::new($user, $this); if ($agg->wasRecentlyCreated) { $this->incrementInstance('participant_count'); @@ -676,6 +676,7 @@ public function startPlay(User $user, PlaylistItem $playlistItem, int $buildId) $playlistItemAgg->updateUserAttempts(); return ScoreToken::create([ + 'beatmap_hash' => get_string($rawParams['beatmap_hash'] ?? null), 'beatmap_id' => $playlistItem->beatmap_id, 'build_id' => $buildId, 'playlist_item_id' => $playlistItem->getKey(), diff --git a/app/Models/ScoreToken.php b/app/Models/ScoreToken.php index f75c4bb07c5..5427c55b505 100644 --- a/app/Models/ScoreToken.php +++ b/app/Models/ScoreToken.php @@ -5,6 +5,7 @@ namespace App\Models; +use App\Exceptions\InvariantException; use App\Models\Multiplayer\PlaylistItem; use App\Models\Solo\Score; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -25,6 +26,8 @@ */ class ScoreToken extends Model { + public ?string $beatmapHash = null; + public function beatmap() { return $this->belongsTo(Beatmap::class, 'beatmap_id'); @@ -74,4 +77,34 @@ public function getAttribute($key) 'user' => $this->getRelationValue($key), }; } + + public function setBeatmapHashAttribute(?string $value): void + { + $this->beatmapHash = $value; + } + + public function assertValid(): void + { + $beatmap = $this->beatmap; + if ($this->beatmapHash !== $beatmap->checksum) { + throw new InvariantException(osu_trans('score_tokens.create.beatmap_hash_invalid')); + } + + $rulesetId = $this->ruleset_id; + if ($rulesetId === null) { + throw new InvariantException('missing ruleset_id'); + } + if (Beatmap::modeStr($rulesetId) === null || !$beatmap->canBeConvertedTo($rulesetId)) { + throw new InvariantException('invalid ruleset_id'); + } + } + + public function save(array $options = []): bool + { + if (!$this->exists) { + $this->assertValid(); + } + + return parent::save($options); + } } diff --git a/database/factories/ScoreTokenFactory.php b/database/factories/ScoreTokenFactory.php index aa7f4950d14..7c7b85f727a 100644 --- a/database/factories/ScoreTokenFactory.php +++ b/database/factories/ScoreTokenFactory.php @@ -23,7 +23,8 @@ public function definition(): array 'build_id' => Build::factory(), 'user_id' => User::factory(), - // depends on beatmap_id + // depend on beatmap_id + 'beatmap_hash' => fn (array $attr) => Beatmap::find($attr['beatmap_id'])->checksum, 'ruleset_id' => fn (array $attr) => Beatmap::find($attr['beatmap_id'])->playmode, ]; } diff --git a/tests/Controllers/Multiplayer/Rooms/Playlist/ScoresControllerTest.php b/tests/Controllers/Multiplayer/Rooms/Playlist/ScoresControllerTest.php index 2477e8d1518..bdbe0d69f72 100644 --- a/tests/Controllers/Multiplayer/Rooms/Playlist/ScoresControllerTest.php +++ b/tests/Controllers/Multiplayer/Rooms/Playlist/ScoresControllerTest.php @@ -169,7 +169,7 @@ public function testUpdate($bodyParams, $status) $playlistItem = PlaylistItem::factory()->create(); $room = $playlistItem->room; $build = Build::factory()->create(['allow_ranking' => true]); - $scoreToken = $room->startPlay($user, $playlistItem, 0); + $scoreToken = static::roomStartPlay($user, $playlistItem); $this->withHeaders(['x-token' => static::createClientToken($build)]); diff --git a/tests/Controllers/ScoreTokensControllerTest.php b/tests/Controllers/ScoreTokensControllerTest.php index e75d971fee0..1dc2c84a305 100644 --- a/tests/Controllers/ScoreTokensControllerTest.php +++ b/tests/Controllers/ScoreTokensControllerTest.php @@ -45,7 +45,7 @@ public function testStore(string $beatmapState, int $status): void /** * @dataProvider dataProviderForTestStoreInvalidParameter */ - public function testStoreInvalidParameter(string $paramKey, ?string $paramValue, int $status): void + public function testStoreInvalidParameter(string $paramKey, ?string $paramValue, int $status, string $errorMessage): void { $origClientCheckVersion = $GLOBALS['cfg']['osu']['client']['check_version']; config_set('osu.client.check_version', true); @@ -78,14 +78,6 @@ public function testStoreInvalidParameter(string $paramKey, ?string $paramValue, $this->expectCountChange(fn () => ScoreToken::count(), 0); - $errorMessage = $paramValue === null ? 'missing' : 'invalid'; - $errorMessage .= ' '; - $errorMessage .= $paramKey === 'client_token' - ? ($paramValue === null - ? 'token header' - : 'client hash' - ) : $paramKey; - $this->json( 'POST', route('api.beatmaps.solo.score-tokens.store', $routeParams), @@ -161,14 +153,14 @@ public static function dataProviderForTestStore(): array public static function dataProviderForTestStoreInvalidParameter(): array { return [ - 'invalid client token' => ['client_token', md5('invalid_'), 422], - 'missing client token' => ['client_token', null, 422], + 'invalid client token' => ['client_token', md5('invalid_'), 422, 'invalid client hash'], + 'missing client token' => ['client_token', null, 422, 'missing token header'], - 'invalid ruleset id' => ['ruleset_id', '5', 422], - 'missing ruleset id' => ['ruleset_id', null, 422], + 'invalid ruleset id' => ['ruleset_id', '5', 422, 'invalid ruleset_id'], + 'missing ruleset id' => ['ruleset_id', null, 422, 'missing ruleset_id'], - 'invalid beatmap hash' => ['beatmap_hash', 'xxx', 422], - 'missing beatmap hash' => ['beatmap_hash', null, 422], + 'invalid beatmap hash' => ['beatmap_hash', 'xxx', 422, 'invalid or missing beatmap_hash'], + 'missing beatmap hash' => ['beatmap_hash', null, 422, 'invalid or missing beatmap_hash'], ]; } diff --git a/tests/Models/Multiplayer/RoomTest.php b/tests/Models/Multiplayer/RoomTest.php index 3a1f8307417..ca8a951478b 100644 --- a/tests/Models/Multiplayer/RoomTest.php +++ b/tests/Models/Multiplayer/RoomTest.php @@ -124,7 +124,7 @@ public function testRoomHasEnded() ]); $this->expectException(InvariantException::class); - $room->startPlay($user, $playlistItem, 0); + static::roomStartPlay($user, $playlistItem); } public function testStartPlay(): void @@ -137,7 +137,7 @@ public function testStartPlay(): void $this->expectCountChange(fn () => $room->userHighScores()->count(), 1); $this->expectCountChange(fn () => $playlistItem->scoreTokens()->count(), 1); - $room->startPlay($user, $playlistItem, 0); + static::roomStartPlay($user, $playlistItem); $room->refresh(); $this->assertSame($user->getKey(), $playlistItem->scoreTokens()->last()->user_id); @@ -150,14 +150,14 @@ public function testMaxAttemptsReached() $playlistItem1 = PlaylistItem::factory()->create(['room_id' => $room]); $playlistItem2 = PlaylistItem::factory()->create(['room_id' => $room]); - $room->startPlay($user, $playlistItem1, 0); + static::roomStartPlay($user, $playlistItem1); $this->assertTrue(true); - $room->startPlay($user, $playlistItem2, 0); + static::roomStartPlay($user, $playlistItem2); $this->assertTrue(true); $this->expectException(InvariantException::class); - $room->startPlay($user, $playlistItem1, 0); + static::roomStartPlay($user, $playlistItem1); } public function testMaxAttemptsForItemReached() @@ -174,19 +174,19 @@ public function testMaxAttemptsForItemReached() ]); $initialCount = $playlistItem1->scoreTokens()->count(); - $room->startPlay($user, $playlistItem1, 0); + static::roomStartPlay($user, $playlistItem1); $this->assertSame($initialCount + 1, $playlistItem1->scoreTokens()->count()); $initialCount = $playlistItem1->scoreTokens()->count(); try { - $room->startPlay($user, $playlistItem1, 0); + static::roomStartPlay($user, $playlistItem1); } catch (Exception $ex) { $this->assertTrue($ex instanceof InvariantException); } $this->assertSame($initialCount, $playlistItem1->scoreTokens()->count()); $initialCount = $playlistItem2->scoreTokens()->count(); - $room->startPlay($user, $playlistItem2, 0); + static::roomStartPlay($user, $playlistItem2); $this->assertSame($initialCount + 1, $playlistItem2->scoreTokens()->count()); } diff --git a/tests/Models/Multiplayer/UserScoreAggregateTest.php b/tests/Models/Multiplayer/UserScoreAggregateTest.php index 710e5d52399..8ab32f33a75 100644 --- a/tests/Models/Multiplayer/UserScoreAggregateTest.php +++ b/tests/Models/Multiplayer/UserScoreAggregateTest.php @@ -173,7 +173,7 @@ public function testStartingPlayIncreasesAttempts(): void $user = User::factory()->create(); $playlistItem = $this->createPlaylistItem(); - $this->room->startPlay($user, $playlistItem, 0); + static::roomStartPlay($user, $playlistItem); $agg = UserScoreAggregate::new($user, $this->room); $this->assertSame(1, $agg->attempts); diff --git a/tests/TestCase.php b/tests/TestCase.php index 03c9ada27fe..1b4af0dae93 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -16,6 +16,7 @@ use App\Models\Multiplayer\PlaylistItem; use App\Models\Multiplayer\ScoreLink; use App\Models\OAuth\Client; +use App\Models\ScoreToken; use App\Models\User; use Artisan; use Carbon\CarbonInterface; @@ -111,7 +112,7 @@ protected static function resetAppDb(DatabaseManager $database): void protected static function roomAddPlay(User $user, PlaylistItem $playlistItem, array $scoreParams): ScoreLink { return $playlistItem->room->completePlay( - $playlistItem->room->startPlay($user, $playlistItem, 0), + static::roomStartPlay($user, $playlistItem), [ 'accuracy' => 0.5, 'beatmap_id' => $playlistItem->beatmap_id, @@ -126,6 +127,16 @@ protected static function roomAddPlay(User $user, PlaylistItem $playlistItem, ar ); } + protected static function roomStartPlay(User $user, PlaylistItem $playlistItem): ScoreToken + { + return $playlistItem->room->startPlay( + $user, + $playlistItem, + 0, + ['beatmap_hash' => $playlistItem->beatmap->checksum] + ); + } + protected function setUp(): void { $this->beforeApplicationDestroyed(fn () => $this->runExpectedCountsCallbacks()); From cdf9c223d6dcedc6ee07cf43bbb9fecfa8c599a5 Mon Sep 17 00:00:00 2001 From: nanaya Date: Wed, 8 Jan 2025 22:15:58 +0900 Subject: [PATCH 059/153] Fix tag controller tests --- tests/TestCase.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/TestCase.php b/tests/TestCase.php index 03c9ada27fe..3617fbd4993 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -135,6 +135,11 @@ protected function setUp(): void // change config setting because we need more than 1 for the tests. config_set('osu.oauth.max_user_clients', 100); + // Disable caching for the BeatmapTagsController and TagsController tests + // because otherwise multiple run of the tests may use stale cache data. + config_set('osu.tags.beatmap_tags_cache_duration', 0); + config_set('osu.tags.tags_cache_duration', 0); + // Force connections to reset even if transactional tests were not used. // Should fix tests going wonky when different queue drivers are used, or anything that // breaks assumptions of object destructor timing. From a148f9f5f4d9e4f71b5f7148c2dcae04b65270c3 Mon Sep 17 00:00:00 2001 From: nanaya Date: Wed, 8 Jan 2025 22:32:46 +0900 Subject: [PATCH 060/153] Inline passing build id --- .../Multiplayer/Rooms/Playlist/ScoresController.php | 7 ++++--- app/Http/Controllers/ScoreTokensController.php | 4 +--- app/Models/Multiplayer/Room.php | 6 +++--- tests/TestCase.php | 10 ++++------ 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php b/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php index 58ea39c74df..19b79dde254 100644 --- a/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php +++ b/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php @@ -182,9 +182,10 @@ public function store($roomId, $playlistId) $user = \Auth::user(); $request = \Request::instance(); - $buildId = ClientCheck::parseToken($request)['buildId']; - - $scoreToken = $room->startPlay($user, $playlistItem, $buildId, $request->all()); + $scoreToken = $room->startPlay($user, $playlistItem, [ + ...$request->all(), + 'build_id' => ClientCheck::parseToken($request)['buildId'], + ]); return json_item($scoreToken, new ScoreTokenTransformer()); } diff --git a/app/Http/Controllers/ScoreTokensController.php b/app/Http/Controllers/ScoreTokensController.php index b135d246d3d..7f352f081aa 100644 --- a/app/Http/Controllers/ScoreTokensController.php +++ b/app/Http/Controllers/ScoreTokensController.php @@ -30,11 +30,9 @@ public function store($beatmapId) $user = auth()->user(); $request = \Request::instance(); - $buildId = ClientCheck::parseToken($request)['buildId']; - $scoreToken = new ScoreToken([ 'beatmap_id' => $beatmap->getKey(), - 'build_id' => $buildId, + 'build_id' => ClientCheck::parseToken($request)['buildId'], 'user_id' => $user->getKey(), ...get_params($request->all(), null, [ 'beatmap_hash', diff --git a/app/Models/Multiplayer/Room.php b/app/Models/Multiplayer/Room.php index 2f2abf7e3c1..30041b14c4b 100644 --- a/app/Models/Multiplayer/Room.php +++ b/app/Models/Multiplayer/Room.php @@ -658,13 +658,13 @@ public function endGame(User $requestingUser) $this->save(); } - public function startPlay(User $user, PlaylistItem $playlistItem, int $buildId, array $rawParams): ScoreToken + public function startPlay(User $user, PlaylistItem $playlistItem, array $rawParams): ScoreToken { priv_check_user($user, 'MultiplayerScoreSubmit', $this)->ensureCan(); $this->assertValidStartPlay($user, $playlistItem); - return $this->getConnection()->transaction(function () use ($buildId, $playlistItem, $rawParams, $user) { + return $this->getConnection()->transaction(function () use ($playlistItem, $rawParams, $user) { $agg = UserScoreAggregate::new($user, $this); if ($agg->wasRecentlyCreated) { $this->incrementInstance('participant_count'); @@ -678,7 +678,7 @@ public function startPlay(User $user, PlaylistItem $playlistItem, int $buildId, return ScoreToken::create([ 'beatmap_hash' => get_string($rawParams['beatmap_hash'] ?? null), 'beatmap_id' => $playlistItem->beatmap_id, - 'build_id' => $buildId, + 'build_id' => $rawParams['build_id'], 'playlist_item_id' => $playlistItem->getKey(), 'ruleset_id' => $playlistItem->ruleset_id, 'user_id' => $user->getKey(), diff --git a/tests/TestCase.php b/tests/TestCase.php index 1b4af0dae93..a2ef32784db 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -129,12 +129,10 @@ protected static function roomAddPlay(User $user, PlaylistItem $playlistItem, ar protected static function roomStartPlay(User $user, PlaylistItem $playlistItem): ScoreToken { - return $playlistItem->room->startPlay( - $user, - $playlistItem, - 0, - ['beatmap_hash' => $playlistItem->beatmap->checksum] - ); + return $playlistItem->room->startPlay($user, $playlistItem, [ + 'beatmap_hash' => $playlistItem->beatmap->checksum, + 'build_id' => 0, + ]); } protected function setUp(): void From 9fc4d20ebd5722c395b08e6d9ba07461bffd9803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 9 Jan 2025 10:22:40 +0100 Subject: [PATCH 061/153] Add completion marker to daily challenge profile counter Implements https://github.com/ppy/osu-web/issues/11597 for the website. --- resources/css/bem/daily-challenge.less | 26 ++++++++++++-- resources/js/profile-page/daily-challenge.tsx | 35 +++++++++++-------- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/resources/css/bem/daily-challenge.less b/resources/css/bem/daily-challenge.less index d0353dd25c1..0d7d2aa6ddf 100644 --- a/resources/css/bem/daily-challenge.less +++ b/resources/css/bem/daily-challenge.less @@ -5,9 +5,29 @@ background: hsl(var(--hsl-b4)); border-radius: @border-radius-large; min-width: 0; - display: flex; - align-items: center; - padding: 3px; + position: relative; + + &--played-today { + border: 2px solid @osu-colour-lime-1; + + &::before { + .fas(); + background-color: @osu-colour-b6; + border-radius: 50%; + color: @osu-colour-lime-1; + content: @fa-var-check-circle; + font-size: 16px; + position: absolute; + right: -8px; + top: -8px; + } + } + + &__content { + display: flex; + align-items: center; + padding: 3px; + } &__name { font-size: @font-size--normal; diff --git a/resources/js/profile-page/daily-challenge.tsx b/resources/js/profile-page/daily-challenge.tsx index 44cf465e2c4..79d5a511704 100644 --- a/resources/js/profile-page/daily-challenge.tsx +++ b/resources/js/profile-page/daily-challenge.tsx @@ -4,6 +4,7 @@ import DailyChallengeUserStatsJson from 'interfaces/daily-challenge-user-stats-json'; import { autorun } from 'mobx'; import { observer } from 'mobx-react'; +import * as moment from 'moment'; import * as React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { classWithModifiers, Modifiers } from 'utils/css'; @@ -122,26 +123,30 @@ export default class DailyChallenge extends React.Component { return null; } + const playedToday = this.props.stats.last_update != null && moment.utc(this.props.stats.last_update).isSame(Date.now(), 'day'); + return (
-
- {trans('users.show.daily_challenge.title').split('\\n').map((line, i) => ( -
{line}
- ))} -
-
-
- {trans( - 'users.show.daily_challenge.unit.day', - { value: formatNumber(this.props.stats.playcount) }, - )} +
+
+ {trans('users.show.daily_challenge.title').split('\\n').map((line, i) => ( +
{line}
+ ))} +
+
+
+ {trans( + 'users.show.daily_challenge.unit.day', + { value: formatNumber(this.props.stats.playcount) }, + )} +
From b8adbac16b6ba1c94a5f114d2a675959076405c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 9 Jan 2025 10:26:06 +0100 Subject: [PATCH 062/153] Adjust radius of inner box As mentioned in https://github.com/ppy/osu-web/issues/11597. --- resources/css/bem/daily-challenge.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/css/bem/daily-challenge.less b/resources/css/bem/daily-challenge.less index 0d7d2aa6ddf..e3e97b527c4 100644 --- a/resources/css/bem/daily-challenge.less +++ b/resources/css/bem/daily-challenge.less @@ -40,7 +40,7 @@ } &__value-box { - border-radius: @border-radius-large; + border-radius: @border-radius-small; background: hsl(var(--hsl-b6)); padding: 5px 10px; } From 5f2ab58224c3654db2ea9e8fbdff270c7036ea8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 9 Jan 2025 10:52:38 +0100 Subject: [PATCH 063/153] Remove superfluous div --- resources/css/bem/daily-challenge.less | 9 ++---- resources/js/profile-page/daily-challenge.tsx | 30 +++++++++---------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/resources/css/bem/daily-challenge.less b/resources/css/bem/daily-challenge.less index e3e97b527c4..ecee003191d 100644 --- a/resources/css/bem/daily-challenge.less +++ b/resources/css/bem/daily-challenge.less @@ -6,6 +6,9 @@ border-radius: @border-radius-large; min-width: 0; position: relative; + display: flex; + align-items: center; + padding: 3px; &--played-today { border: 2px solid @osu-colour-lime-1; @@ -23,12 +26,6 @@ } } - &__content { - display: flex; - align-items: center; - padding: 3px; - } - &__name { font-size: @font-size--normal; padding: 0 5px; diff --git a/resources/js/profile-page/daily-challenge.tsx b/resources/js/profile-page/daily-challenge.tsx index 79d5a511704..73c380bf9ec 100644 --- a/resources/js/profile-page/daily-challenge.tsx +++ b/resources/js/profile-page/daily-challenge.tsx @@ -131,22 +131,20 @@ export default class DailyChallenge extends React.Component { className={classWithModifiers('daily-challenge', { 'played-today': playedToday })} onMouseOver={this.onMouseOver} > -
-
- {trans('users.show.daily_challenge.title').split('\\n').map((line, i) => ( -
{line}
- ))} -
-
-
- {trans( - 'users.show.daily_challenge.unit.day', - { value: formatNumber(this.props.stats.playcount) }, - )} -
+
+ {trans('users.show.daily_challenge.title').split('\\n').map((line, i) => ( +
{line}
+ ))} +
+
+
+ {trans( + 'users.show.daily_challenge.unit.day', + { value: formatNumber(this.props.stats.playcount) }, + )}
From 639c29ba3f2194255c1b251d2dc9f2fa83f8dffc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 15:37:48 +0000 Subject: [PATCH 064/153] Bump nesbot/carbon from 2.72.5 to 2.72.6 Bumps [nesbot/carbon](https://github.com/CarbonPHP/carbon) from 2.72.5 to 2.72.6. - [Release notes](https://github.com/CarbonPHP/carbon/releases) - [Commits](https://github.com/CarbonPHP/carbon/compare/2.72.5...2.72.6) --- updated-dependencies: - dependency-name: nesbot/carbon dependency-type: indirect ... Signed-off-by: dependabot[bot] --- composer.lock | 134 +++++++++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/composer.lock b/composer.lock index a5f11026f9f..97ce1ad5739 100644 --- a/composer.lock +++ b/composer.lock @@ -5060,16 +5060,16 @@ }, { "name": "nesbot/carbon", - "version": "2.72.5", + "version": "2.72.6", "source": { "type": "git", - "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "afd46589c216118ecd48ff2b95d77596af1e57ed" + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "1e9d50601e7035a4c61441a208cb5bed73e108c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/afd46589c216118ecd48ff2b95d77596af1e57ed", - "reference": "afd46589c216118ecd48ff2b95d77596af1e57ed", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/1e9d50601e7035a4c61441a208cb5bed73e108c5", + "reference": "1e9d50601e7035a4c61441a208cb5bed73e108c5", "shasum": "" }, "require": { @@ -5089,7 +5089,7 @@ "doctrine/orm": "^2.7 || ^3.0", "friendsofphp/php-cs-fixer": "^3.0", "kylekatarnls/multi-tester": "^2.0", - "ondrejmirtes/better-reflection": "*", + "ondrejmirtes/better-reflection": "<6", "phpmd/phpmd": "^2.9", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^0.12.99 || ^1.7.14", @@ -5102,10 +5102,6 @@ ], "type": "library", "extra": { - "branch-alias": { - "dev-master": "3.x-dev", - "dev-2.x": "2.x-dev" - }, "laravel": { "providers": [ "Carbon\\Laravel\\ServiceProvider" @@ -5115,6 +5111,10 @@ "includes": [ "extension.neon" ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" } }, "autoload": { @@ -5163,7 +5163,7 @@ "type": "tidelift" } ], - "time": "2024-06-03T19:18:41+00:00" + "time": "2024-12-27T09:28:11+00:00" }, { "name": "nette/schema", @@ -8073,16 +8073,16 @@ }, { "name": "symfony/cache-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/cache-contracts.git", - "reference": "df6a1a44c890faded49a5fca33c2d5c5fd3c2197" + "reference": "15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/df6a1a44c890faded49a5fca33c2d5c5fd3c2197", - "reference": "df6a1a44c890faded49a5fca33c2d5c5fd3c2197", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b", + "reference": "15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b", "shasum": "" }, "require": { @@ -8091,12 +8091,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -8129,7 +8129,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/cache-contracts/tree/v3.5.1" }, "funding": [ { @@ -8145,7 +8145,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/console", @@ -8325,12 +8325,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -8530,16 +8530,16 @@ }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", - "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", "shasum": "" }, "require": { @@ -8548,12 +8548,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -8586,7 +8586,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" }, "funding": [ { @@ -8602,7 +8602,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/finder", @@ -8763,16 +8763,16 @@ }, { "name": "symfony/http-client-contracts", - "version": "v3.5.0", + "version": "v3.5.2", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "20414d96f391677bf80078aa55baece78b82647d" + "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/20414d96f391677bf80078aa55baece78b82647d", - "reference": "20414d96f391677bf80078aa55baece78b82647d", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ee8d807ab20fcb51267fdace50fbe3494c31e645", + "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645", "shasum": "" }, "require": { @@ -8780,12 +8780,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -8821,7 +8821,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.2" }, "funding": [ { @@ -8837,7 +8837,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-12-07T08:49:48+00:00" }, { "name": "symfony/http-foundation", @@ -9609,8 +9609,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -10133,16 +10133,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", "shasum": "" }, "require": { @@ -10155,12 +10155,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -10196,7 +10196,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" }, "funding": [ { @@ -10212,7 +10212,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/string", @@ -10398,16 +10398,16 @@ }, { "name": "symfony/translation-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a" + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", - "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", "shasum": "" }, "require": { @@ -10415,12 +10415,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -10456,7 +10456,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" }, "funding": [ { @@ -10472,7 +10472,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/uid", From 58514a67d1f38e9842045615993252a8810fd50b Mon Sep 17 00:00:00 2001 From: patriot720 Date: Thu, 9 Jan 2025 17:43:30 +0300 Subject: [PATCH 065/153] Add ability to search by circle or slider count --- app/Libraries/Search/BeatmapsetQueryParser.php | 6 ++++++ app/Libraries/Search/BeatmapsetSearch.php | 2 ++ app/Libraries/Search/BeatmapsetSearchParams.php | 2 ++ app/Libraries/Search/BeatmapsetSearchRequestParams.php | 2 ++ 4 files changed, 12 insertions(+) diff --git a/app/Libraries/Search/BeatmapsetQueryParser.php b/app/Libraries/Search/BeatmapsetQueryParser.php index faa4f51b8cd..24d3e16b040 100644 --- a/app/Libraries/Search/BeatmapsetQueryParser.php +++ b/app/Libraries/Search/BeatmapsetQueryParser.php @@ -41,6 +41,12 @@ public static function parse(?string $query): array case 'bpm': $option = static::makeFloatRangeOption($op, $m['value'], 0.01 / 2); break; + case 'circles': + $option = static::makeIntRangeOption($op, $m['value']); + break; + case 'sliders': + $option = static::makeIntRangeOption($op, $m['value']); + break; case 'length': $parsed = get_length_seconds($m['value']); if ($parsed !== null) { diff --git a/app/Libraries/Search/BeatmapsetSearch.php b/app/Libraries/Search/BeatmapsetSearch.php index f88edc7f930..96857e5777f 100644 --- a/app/Libraries/Search/BeatmapsetSearch.php +++ b/app/Libraries/Search/BeatmapsetSearch.php @@ -307,6 +307,8 @@ private function addSimpleFilters(BoolQuery $query, BoolQuery $nested): void 'accuracy' => ['field' => 'beatmaps.diff_overall', 'type' => 'range'], 'ar' => ['field' => 'beatmaps.diff_approach', 'type' => 'range'], 'bpm' => ['field' => 'bpm', 'type' => 'range'], + 'countNormal' => ['field' => 'beatmaps.countNormal', 'type' => 'range'], + 'countSlider' => ['field' => 'beatmaps.countSlider', 'type' => 'range'], 'created' => ['field' => 'submit_date', 'type' => 'range'], 'cs' => ['field' => 'beatmaps.diff_size', 'type' => 'range'], 'difficultyRating' => ['field' => 'beatmaps.difficultyrating', 'type' => 'range'], diff --git a/app/Libraries/Search/BeatmapsetSearchParams.php b/app/Libraries/Search/BeatmapsetSearchParams.php index 9cbd9b9e0d9..03cce272fbd 100644 --- a/app/Libraries/Search/BeatmapsetSearchParams.php +++ b/app/Libraries/Search/BeatmapsetSearchParams.php @@ -19,6 +19,8 @@ class BeatmapsetSearchParams extends SearchParams public ?array $ar = null; public ?string $artist = null; public ?array $bpm = null; + public ?array $countNormal = null; + public ?array $countSlider = null; public ?array $created = null; public ?string $creator = null; public ?array $cs = null; diff --git a/app/Libraries/Search/BeatmapsetSearchRequestParams.php b/app/Libraries/Search/BeatmapsetSearchRequestParams.php index c070c61139e..935295c2f0b 100644 --- a/app/Libraries/Search/BeatmapsetSearchRequestParams.php +++ b/app/Libraries/Search/BeatmapsetSearchRequestParams.php @@ -210,6 +210,7 @@ private function parseQuery(): void 'ar' => 'ar', 'artist' => 'artist', 'bpm' => 'bpm', + 'circles' => 'countNormal', 'created' => 'created', 'creator' => 'creator', 'cs' => 'cs', @@ -221,6 +222,7 @@ private function parseQuery(): void 'length' => 'totalLength', 'od' => 'accuracy', 'ranked' => 'ranked', + 'sliders' => 'countSlider', 'source' => 'source', 'stars' => 'difficultyRating', 'status' => 'statusRange', From fdf2e5822ab9a4b9d5edbaa0bb29e8395d684a74 Mon Sep 17 00:00:00 2001 From: nanaya Date: Fri, 10 Jan 2025 17:47:32 +0900 Subject: [PATCH 066/153] Fix env file setup for dusk.local Also fixed bug of github token being echoed to console if set. Note that the APP_ENV in .env.dusk.local isn't actually used when using docker compose as it overrides the value with the one in .env. --- docker/development/prepare.sh | 41 +++++++++++++---------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/docker/development/prepare.sh b/docker/development/prepare.sh index 7f69ea08701..710db995174 100755 --- a/docker/development/prepare.sh +++ b/docker/development/prepare.sh @@ -15,18 +15,24 @@ _run() { docker compose run --rm php "$@" } -_run_dusk() { - docker compose run --rm -e APP_ENV=dusk.local php "$@" -} - -if [ ! -f .env ]; then - echo "Copying default env file" - cp .env.example .env -fi +for envfile in .env .env.testing .env.dusk.local; do + if [ ! -f "${envfile}" ]; then + echo "Copying env file '${envfile}'" + cp "${envfile}.example" "${envfile}" + fi + if [ "${envfile}" != .env.testing ] && ! grep -q '^APP_KEY=.' "${envfile}"; then + echo "Generating app key for env file '${envfile}'" + sed -i -e '/^APP_KEY=.*/d' "${envfile}" + : "${APP_KEY="base64:$(head -c 32 /dev/urandom | base64)"}" + echo "APP_KEY=${APP_KEY}" >> "${envfile}" + fi +done if [ -n "${GITHUB_TOKEN:-}" ]; then _run composer config -g github-oauth.github.com "${GITHUB_TOKEN}" - grep ^GITHUB_TOKEN= .env || echo "GITHUB_TOKEN=${GITHUB_TOKEN}" >> .env + for envfile in .env .env.dusk.local; do + grep -q '^GITHUB_TOKEN=' "${envfile}" || echo "GITHUB_TOKEN=${GITHUB_TOKEN}" >> "${envfile}" + done fi docker compose build @@ -37,23 +43,6 @@ _run composer install _run artisan dusk:chrome-driver -if ! grep -q '^APP_KEY=.' .env; then - echo "Generating app key" - _run artisan key:generate -fi - -if [ ! -f .env.testing ]; then - echo "Copying default test env file" - cp .env.testing.example .env.testing -fi - -if [ ! -f .env.dusk.local ]; then - echo "Copying default dusk env file" - cp .env.dusk.local.example .env.dusk.local - echo "Generating app key for dusk" - _run_dusk artisan key:generate -fi - if [ -d storage/oauth-public.key ]; then echo "oauth-public.key is a directory. Removing it" rmdir storage/oauth-public.key From 298f2acea9e9d80c0e9faf62ab702e09a3c68185 Mon Sep 17 00:00:00 2001 From: nanaya Date: Fri, 10 Jan 2025 19:04:52 +0900 Subject: [PATCH 067/153] Quotes optional --- docker/development/prepare.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/development/prepare.sh b/docker/development/prepare.sh index 710db995174..94ba4806019 100755 --- a/docker/development/prepare.sh +++ b/docker/development/prepare.sh @@ -23,7 +23,7 @@ for envfile in .env .env.testing .env.dusk.local; do if [ "${envfile}" != .env.testing ] && ! grep -q '^APP_KEY=.' "${envfile}"; then echo "Generating app key for env file '${envfile}'" sed -i -e '/^APP_KEY=.*/d' "${envfile}" - : "${APP_KEY="base64:$(head -c 32 /dev/urandom | base64)"}" + : ${APP_KEY="base64:$(head -c 32 /dev/urandom | base64)"} echo "APP_KEY=${APP_KEY}" >> "${envfile}" fi done From d0be2dc1c67aca7e5b7b8d1c98cb3db3069725e4 Mon Sep 17 00:00:00 2001 From: nanaya Date: Fri, 10 Jan 2025 22:27:57 +0900 Subject: [PATCH 068/153] Add interop endpoint to create and join multiplayer room --- .../InterOp/Multiplayer/RoomsController.php | 35 ++++++ .../Multiplayer/RoomsController.php | 47 ++------ app/Models/Multiplayer/Room.php | 12 +++ .../Multiplayer/RoomTransformer.php | 20 ++++ app/helpers.php | 8 +- routes/web.php | 5 + .../Multiplayer/RoomsControllerTest.php | 101 ++++++++++++++++++ tests/TestCase.php | 15 ++- 8 files changed, 200 insertions(+), 43 deletions(-) create mode 100644 app/Http/Controllers/InterOp/Multiplayer/RoomsController.php create mode 100644 tests/Controllers/InterOp/Multiplayer/RoomsControllerTest.php diff --git a/app/Http/Controllers/InterOp/Multiplayer/RoomsController.php b/app/Http/Controllers/InterOp/Multiplayer/RoomsController.php new file mode 100644 index 00000000000..27e08dfbb9c --- /dev/null +++ b/app/Http/Controllers/InterOp/Multiplayer/RoomsController.php @@ -0,0 +1,35 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +namespace App\Http\Controllers\InterOp\Multiplayer; + +use App\Http\Controllers\Controller; +use App\Models\Multiplayer\Room; +use App\Models\User; +use App\Transformers\Multiplayer\RoomTransformer; + +class RoomsController extends Controller +{ + public function join(string $id, string $userId) + { + $user = User::findOrFail($userId); + $room = Room::findOrFail($id); + + $room->assertCorrectPassword(get_string(request('password'))); + $room->join($user); + + return RoomTransformer::createShowResponse($room); + } + + public function store() + { + $params = \Request::all(); + $user = User::findOrFail(get_int($params['user_id'] ?? null)); + + $room = (new Room())->startGame($user, $params); + + return RoomTransformer::createShowResponse($room); + } +} diff --git a/app/Http/Controllers/Multiplayer/RoomsController.php b/app/Http/Controllers/Multiplayer/RoomsController.php index 97c6e7fd555..00d0bdd7c13 100644 --- a/app/Http/Controllers/Multiplayer/RoomsController.php +++ b/app/Http/Controllers/Multiplayer/RoomsController.php @@ -5,7 +5,6 @@ namespace App\Http\Controllers\Multiplayer; -use App\Exceptions\InvariantException; use App\Http\Controllers\Controller; use App\Http\Controllers\Ranking\DailyChallengeController; use App\Models\Model; @@ -101,24 +100,18 @@ public function index() public function join($roomId, $userId) { + $currentUser = \Auth::user(); // this allows admins/whatever to add users to games in the future - if (get_int($userId) !== auth()->user()->user_id) { + if (get_int($userId) !== $currentUser->getKey()) { abort(403); } $room = Room::findOrFail($roomId); + $room->assertCorrectPassword(get_string(request('password'))); - if ($room->password !== null) { - $password = get_param_value(request('password'), null); - - if ($password === null || !hash_equals(hash('sha256', $room->password), hash('sha256', $password))) { - abort(403, osu_trans('multiplayer.room.invalid_password')); - } - } - - $room->join(auth()->user()); + $room->join($currentUser); - return $this->createJoinedRoomResponse($room); + return RoomTransformer::createShowResponse($room); } public function leaderboard($roomId) @@ -168,7 +161,7 @@ public function show($id) } if (is_api_request()) { - return $this->createJoinedRoomResponse($room); + return RoomTransformer::createShowResponse($room); } if ($room->category === 'daily_challenge') { @@ -200,32 +193,8 @@ public function show($id) public function store() { - try { - $room = (new Room())->startGame(auth()->user(), request()->all()); - - return $this->createJoinedRoomResponse($room); - } catch (InvariantException $e) { - return error_popup($e->getMessage(), $e->getStatusCode()); - } - } + $room = (new Room())->startGame(\Auth::user(), \Request::all()); - private function createJoinedRoomResponse($room) - { - return json_item( - $room->loadMissing([ - 'host', - 'playlist.beatmap.beatmapset', - 'playlist.beatmap.baseMaxCombo', - ]), - 'Multiplayer\Room', - [ - 'current_user_score.playlist_item_attempts', - 'host.country', - 'playlist.beatmap.beatmapset', - 'playlist.beatmap.checksum', - 'playlist.beatmap.max_combo', - 'recent_participants', - ] - ); + return RoomTransformer::createShowResponse($room); } } diff --git a/app/Models/Multiplayer/Room.php b/app/Models/Multiplayer/Room.php index c50ed5d63e3..a4aaedb1f82 100644 --- a/app/Models/Multiplayer/Room.php +++ b/app/Models/Multiplayer/Room.php @@ -6,6 +6,7 @@ namespace App\Models\Multiplayer; use App\Casts\PresentString; +use App\Exceptions\AuthorizationException; use App\Exceptions\InvariantException; use App\Models\Beatmap; use App\Models\Chat\Channel; @@ -332,6 +333,17 @@ public function scopeWithRecentParticipantIds($query, ?int $limit = null) ", 'recent_participant_ids'); } + public function assertCorrectPassword(?string $password): void + { + if ($this->password === null) { + return; + } + + if ($password === null || !hash_equals(hash('sha256', $this->password), hash('sha256', $password))) { + throw new AuthorizationException(osu_trans('multiplayer.room.invalid_password')); + } + } + public function difficultyRange() { $extraQuery = true; diff --git a/app/Transformers/Multiplayer/RoomTransformer.php b/app/Transformers/Multiplayer/RoomTransformer.php index c96cae14b56..cc3d0cf93c1 100644 --- a/app/Transformers/Multiplayer/RoomTransformer.php +++ b/app/Transformers/Multiplayer/RoomTransformer.php @@ -23,6 +23,26 @@ class RoomTransformer extends TransformerAbstract 'recent_participants', ]; + public static function createShowResponse(Room $room): array + { + return json_item( + $room->loadMissing([ + 'host', + 'playlist.beatmap.baseMaxCombo', + 'playlist.beatmap.beatmapset', + ]), + new static(), + [ + 'current_user_score.playlist_item_attempts', + 'host.country', + 'playlist.beatmap.beatmapset', + 'playlist.beatmap.checksum', + 'playlist.beatmap.max_combo', + 'recent_participants', + ], + ); + } + public function transform(Room $room) { return [ diff --git a/app/helpers.php b/app/helpers.php index 81685d9c2bc..2a808374167 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -840,7 +840,9 @@ function forum_user_link(int $id, string $username, string|null $colour, int|nul function is_api_request(): bool { - return str_starts_with(rawurldecode(Request::getPathInfo()), '/api/'); + $url = rawurldecode(Request::getPathInfo()); + return str_starts_with($url, '/api/') + || str_starts_with($url, '/_lio/'); } function is_http(string $url): bool @@ -1718,6 +1720,10 @@ function parse_time_to_carbon($value) if ($value instanceof DateTime) { return Carbon\Carbon::instance($value); } + + if ($value instanceof Carbon\CarbonImmutable) { + return $value->toMutable(); + } } function format_duration_for_display(int $seconds) diff --git a/routes/web.php b/routes/web.php index 2dc8ccb6689..6437bcae2d1 100644 --- a/routes/web.php +++ b/routes/web.php @@ -602,6 +602,11 @@ Route::apiResource('bulk', 'Indexing\BulkController', ['only' => ['store']]); }); + Route::group(['as' => 'multiplayer.', 'namespace' => 'Multiplayer', 'prefix' => 'multiplayer'], function () { + Route::put('rooms/{room}/users/{user}', 'RoomsController@join')->name('rooms.join'); + Route::apiResource('rooms', 'RoomsController', ['only' => ['store']]); + }); + Route::post('user-achievement/{user}/{achievement}/{beatmap?}', 'UsersController@achievement')->name('users.achievement'); Route::group(['as' => 'user-group.'], function () { diff --git a/tests/Controllers/InterOp/Multiplayer/RoomsControllerTest.php b/tests/Controllers/InterOp/Multiplayer/RoomsControllerTest.php new file mode 100644 index 00000000000..353e1902eea --- /dev/null +++ b/tests/Controllers/InterOp/Multiplayer/RoomsControllerTest.php @@ -0,0 +1,101 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +namespace Tests\Controllers\InterOp\Multiplayer; + +use App\Models\Beatmap; +use App\Models\Chat\UserChannel; +use App\Models\Multiplayer\Room; +use App\Models\User; +use Carbon\CarbonImmutable; +use Tests\TestCase; + +class RoomsControllerTest extends TestCase +{ + private static function startRoomParams(): array + { + $beatmap = Beatmap::factory()->create(); + + return [ + 'ends_at' => CarbonImmutable::now()->addHours(1), + 'name' => 'test room', + 'type' => Room::REALTIME_DEFAULT_TYPE, + 'playlist' => [[ + 'beatmap_id' => $beatmap->getKey(), + 'ruleset_id' => $beatmap->playmode, + ]], + ]; + } + + public function testJoin(): void + { + $room = (new Room())->startGame(User::factory()->create(), static::startRoomParams()); + $user = User::factory()->create(); + + $this->expectCountChange(fn () => UserChannel::count(), 1); + + $this->withInterOpHeader( + route('interop.multiplayer.rooms.join', [ + 'room' => $room->getKey(), + 'user' => $user->getKey(), + ]), + fn ($url) => $this->put($url), + )->assertSuccessful(); + } + + public function testJoinWithPassword(): void + { + $room = (new Room())->startGame(User::factory()->create(), [ + ...static::startRoomParams(), + 'password' => 'hunter2', + ]); + $user = User::factory()->create(); + + $this->expectCountChange(fn () => UserChannel::count(), 1); + + $this->withInterOpHeader( + route('interop.multiplayer.rooms.join', [ + 'room' => $room->getKey(), + 'user' => $user->getKey(), + ]), + fn ($url) => $this->put($url, ['password' => 'hunter2']), + )->assertSuccessful(); + } + + public function testJoinWithPasswordInvalid(): void + { + $room = (new Room())->startGame(User::factory()->create(), [ + ...static::startRoomParams(), + 'password' => 'hunter2', + ]); + $user = User::factory()->create(); + + $this->expectCountChange(fn () => UserChannel::count(), 0); + + $this->withInterOpHeader( + route('interop.multiplayer.rooms.join', [ + 'room' => $room->getKey(), + 'user' => $user->getKey(), + ]), + fn ($url) => $this->put($url, ['password' => '*******']), + )->assertStatus(403); + } + + public function testStore(): void + { + $beatmap = Beatmap::factory()->create(); + $params = [ + ...static::startRoomParams(), + 'user_id' => User::factory()->create()->getKey(), + ]; + + $this->expectCountChange(fn () => Room::count(), 1); + + $this->withInterOpHeader( + route('interop.multiplayer.rooms.store'), + fn ($url) => $this->post($url, $params), + )->assertSuccessful(); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 3617fbd4993..f706a8427aa 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -365,11 +365,20 @@ protected function runFakeQueue() $this->invokeSetProperty(app('queue'), 'jobs', []); } - protected function withInterOpHeader($url) + protected function withInterOpHeader($url, ?callable $callback = null) { - return $this->withHeaders([ - 'X-LIO-Signature' => hash_hmac('sha1', $url, $GLOBALS['cfg']['osu']['legacy']['shared_interop_secret']), + if ($callback === null) { + $timestampedUrl = $url; + } else { + $connector = strpos($url, '?') === false ? '?' : '&'; + $timestampedUrl = $url.$connector.'timestamp='.time(); + } + + $this->withHeaders([ + 'X-LIO-Signature' => hash_hmac('sha1', $timestampedUrl, $GLOBALS['cfg']['osu']['legacy']['shared_interop_secret']), ]); + + return $callback === null ? $this : $callback($timestampedUrl); } protected function withPersistentSession(SessionStore $session): static From 4751d8e119d2861676815ce9da3911e8f6235705 Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Sun, 12 Jan 2025 18:45:25 +0100 Subject: [PATCH 069/153] move score factors to seasons database column --- app/Models/Season.php | 24 +++++------------ app/Models/SeasonScoreFactor.php | 22 --------------- .../factories/SeasonScoreFactorFactory.php | 22 --------------- ...2_172229_add_score_factors_on_seasons.php} | 12 ++++----- tests/Models/UserSeasonScoreTest.php | 27 +++---------------- 5 files changed, 14 insertions(+), 93 deletions(-) delete mode 100644 app/Models/SeasonScoreFactor.php delete mode 100644 database/factories/SeasonScoreFactorFactory.php rename database/migrations/{2024_10_23_173228_create_season_score_factors_table.php => 2025_01_12_172229_add_score_factors_on_seasons.php} (67%) diff --git a/app/Models/Season.php b/app/Models/Season.php index 52e04f368ac..9981d7e3f35 100644 --- a/app/Models/Season.php +++ b/app/Models/Season.php @@ -10,18 +10,19 @@ use Carbon\Carbon; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\Relations\BelongsToMany; -use Illuminate\Database\Eloquent\Relations\HasMany; /** * @property bool $finalised * @property string $name * @property-read Collection $rooms + * @property float[]|null $score_factors * @property string|null $url */ class Season extends Model { protected $casts = [ 'finalised' => 'boolean', + 'score_factors' => 'array', ]; public function scopeActive($query) @@ -51,25 +52,12 @@ public function endDate(): ?Carbon : null; } - public function scoreFactors(): HasMany - { - return $this->hasMany(SeasonScoreFactor::class); - } - public function scoreFactorsOrderedForCalculation(): array { - return cache_remember_mutexed( - "score_factors:{$this->id}", - $GLOBALS['cfg']['osu']['seasons']['factors_cache_duration'], - [], - function () { - return $this->scoreFactors() - ->orderByDesc('factor') - ->get() - ->pluck('factor') - ->toArray(); - } - ); + $factors = $this->score_factors ?? []; + rsort($factors); + + return $factors; } public function startDate(): ?Carbon diff --git a/app/Models/SeasonScoreFactor.php b/app/Models/SeasonScoreFactor.php deleted file mode 100644 index bd270094fa8..00000000000 --- a/app/Models/SeasonScoreFactor.php +++ /dev/null @@ -1,22 +0,0 @@ -. Licensed under the GNU Affero General Public License v3.0. -// See the LICENCE file in the repository root for full licence text. - -declare(strict_types=1); - -namespace App\Models; - -use Illuminate\Database\Eloquent\Factories\HasFactory; - -/** - * @property int $id - * @property float $factor - * @property int $season_id - */ -class SeasonScoreFactor extends Model -{ - use HasFactory; - - public $timestamps = false; -} diff --git a/database/factories/SeasonScoreFactorFactory.php b/database/factories/SeasonScoreFactorFactory.php deleted file mode 100644 index 1df687bdf53..00000000000 --- a/database/factories/SeasonScoreFactorFactory.php +++ /dev/null @@ -1,22 +0,0 @@ -. Licensed under the GNU Affero General Public License v3.0. -// See the LICENCE file in the repository root for full licence text. - -declare(strict_types=1); - -namespace Database\Factories; - -use App\Models\SeasonScoreFactor; - -class SeasonScoreFactorFactory extends Factory -{ - protected $model = SeasonScoreFactor::class; - - public function definition(): array - { - return [ - 'factor' => 1, - ]; - } -} diff --git a/database/migrations/2024_10_23_173228_create_season_score_factors_table.php b/database/migrations/2025_01_12_172229_add_score_factors_on_seasons.php similarity index 67% rename from database/migrations/2024_10_23_173228_create_season_score_factors_table.php rename to database/migrations/2025_01_12_172229_add_score_factors_on_seasons.php index 9adca317b84..cf4a4bf6d91 100644 --- a/database/migrations/2024_10_23_173228_create_season_score_factors_table.php +++ b/database/migrations/2025_01_12_172229_add_score_factors_on_seasons.php @@ -3,8 +3,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. // See the LICENCE file in the repository root for full licence text. -declare(strict_types=1); - use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; @@ -16,10 +14,8 @@ */ public function up(): void { - Schema::create('season_score_factors', function (Blueprint $table) { - $table->id(); - $table->integer('season_id'); - $table->float('factor'); + Schema::table('seasons', function (Blueprint $table) { + $table->json('score_factors')->nullable(); }); } @@ -28,6 +24,8 @@ public function up(): void */ public function down(): void { - Schema::dropIfExists('season_score_factors'); + Schema::table('seasons', function (Blueprint $table) { + $table->dropColumn('score_factors'); + }); } }; diff --git a/tests/Models/UserSeasonScoreTest.php b/tests/Models/UserSeasonScoreTest.php index 11e5f9d9eab..4276550be2a 100644 --- a/tests/Models/UserSeasonScoreTest.php +++ b/tests/Models/UserSeasonScoreTest.php @@ -11,7 +11,6 @@ use App\Models\Multiplayer\Room; use App\Models\Season; use App\Models\SeasonRoom; -use App\Models\SeasonScoreFactor; use App\Models\User; use App\Models\UserSeasonScore; use Tests\TestCase; @@ -23,13 +22,6 @@ class UserSeasonScoreTest extends TestCase public function testAddMultipleScores(): void { - foreach ([1, 0.75, 0.5] as $factor) { - SeasonScoreFactor::factory()->create([ - 'factor' => $factor, - 'season_id' => $this->season, - ]); - } - $this->createRoomWithPlay(10); $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) @@ -51,13 +43,6 @@ public function testAddMultipleScores(): void public function testAddMultipleScoresWithChildrenRooms(): void { - foreach ([1, 0.75, 0.5] as $factor) { - SeasonScoreFactor::factory()->create([ - 'factor' => $factor, - 'season_id' => $this->season, - ]); - } - $firstRoom = $this->createRoomWithPlay(10); $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) @@ -94,8 +79,6 @@ public function testAddMultipleScoresWithChildrenRooms(): void public function testAddHigherScoreInChildRoom(): void { - SeasonScoreFactor::factory()->create(['season_id' => $this->season]); - $room = $this->createRoomWithPlay(10); $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) @@ -112,8 +95,6 @@ public function testAddHigherScoreInChildRoom(): void public function testAddHigherScoreInParentRoom(): void { - SeasonScoreFactor::factory()->create(['season_id' => $this->season]); - $room = $this->createRoomWithPlay(15); $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) @@ -130,8 +111,6 @@ public function testAddHigherScoreInParentRoom(): void public function testAddSameScoreInChildAndParentRoom(): void { - SeasonScoreFactor::factory()->create(['season_id' => $this->season]); - $room = $this->createRoomWithPlay(10); $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) @@ -148,8 +127,6 @@ public function testAddSameScoreInChildAndParentRoom(): void public function testAddScoreInChildRoomOnly(): void { - SeasonScoreFactor::factory()->create(['season_id' => $this->season]); - $room = $this->createRoom(); $this->createRoomWithPlay(10, $room->getKey()); @@ -164,7 +141,9 @@ protected function setUp(): void { parent::setUp(); - $this->season = Season::factory()->create(); + $this->season = Season::factory()->create([ + 'score_factors' => [1, 0.75, 0.5], + ]); $this->user = User::factory()->create(); } From bade39ad0914b5c5adb67331cc2494762b004b33 Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Sun, 12 Jan 2025 22:45:39 +0100 Subject: [PATCH 070/153] group rooms in SeasonRoom instead of parent_id --- app/Models/SeasonRoom.php | 1 + app/Models/UserSeasonScore.php | 33 +++++------- ...0_add_group_indicator_on_season_rooms.php} | 13 ++--- tests/Models/UserSeasonScoreTest.php | 54 +++++++++++-------- 4 files changed, 51 insertions(+), 50 deletions(-) rename database/migrations/{2024_10_22_194840_add_parent_id_to_rooms.php => 2025_01_12_174650_add_group_indicator_on_season_rooms.php} (61%) diff --git a/app/Models/SeasonRoom.php b/app/Models/SeasonRoom.php index a5bbfc281c3..65758e30231 100644 --- a/app/Models/SeasonRoom.php +++ b/app/Models/SeasonRoom.php @@ -11,6 +11,7 @@ use Illuminate\Database\Eloquent\Model; /** + * @property string|null $group_indicator * @property int $id * @property int $room_id * @property int $season_id diff --git a/app/Models/UserSeasonScore.php b/app/Models/UserSeasonScore.php index 51825ef866c..7a2701c2c66 100644 --- a/app/Models/UserSeasonScore.php +++ b/app/Models/UserSeasonScore.php @@ -26,38 +26,31 @@ class UserSeasonScore extends Model public function calculate(): void { - $userScores = UserScoreAggregate::whereIn('room_id', $this->season->rooms->pluck('id')) + $rooms = $this->season->rooms() + ->withPivot('group_indicator') + ->get(); + + $userScores = UserScoreAggregate::whereIn('room_id', $rooms->pluck('id')) ->where('user_id', $this->user_id) ->get(); $factors = $this->season->scoreFactorsOrderedForCalculation(); - $parentRooms = $this->season->rooms->where('parent_id', null); + $roomsGrouped = $rooms->groupBy('pivot.group_indicator'); - if ($parentRooms->count() > count($factors)) { + if ($roomsGrouped->count() > count($factors)) { throw new InvariantException(osu_trans('rankings.seasons.validation.not_enough_factors')); } - $scores = []; - - foreach ($parentRooms as $room) { - $totalScore = $userScores->where('room_id', $room->getKey()) - ->first() - ?->total_score; - - $childRoomId = $this->season->rooms - ->where('parent_id', $room->getKey()) - ->first() - ?->getKey(); - - $totalScoreChild = $userScores->where('room_id', $childRoomId) - ->first() - ?->total_score; + foreach ($roomsGrouped as $rooms) { + $groupUserScores = $userScores + ->whereIn('room_id', $rooms->pluck('id')) + ->pluck('total_score'); - if ($totalScore === null && $totalScoreChild === null) { + if ($groupUserScores === null) { continue; } - $scores[] = max([$totalScore, $totalScoreChild]); + $scores[] = $groupUserScores->max(); } rsort($scores); diff --git a/database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php b/database/migrations/2025_01_12_174650_add_group_indicator_on_season_rooms.php similarity index 61% rename from database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php rename to database/migrations/2025_01_12_174650_add_group_indicator_on_season_rooms.php index 29bea618cf6..547e7cefd2a 100644 --- a/database/migrations/2024_10_22_194840_add_parent_id_to_rooms.php +++ b/database/migrations/2025_01_12_174650_add_group_indicator_on_season_rooms.php @@ -3,8 +3,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. // See the LICENCE file in the repository root for full licence text. -declare(strict_types=1); - use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; @@ -16,11 +14,8 @@ */ public function up(): void { - Schema::table('multiplayer_rooms', function (Blueprint $table) { - $table->bigInteger('parent_id') - ->unsigned() - ->nullable() - ->unique(); + Schema::table('season_rooms', function (Blueprint $table) { + $table->string('group_indicator')->nullable(); }); } @@ -29,8 +24,8 @@ public function up(): void */ public function down(): void { - Schema::table('multiplayer_rooms', function (Blueprint $table) { - $table->dropColumn('parent_id'); + Schema::table('season_rooms', function (Blueprint $table) { + $table->dropColumn('group_indicator'); }); } }; diff --git a/tests/Models/UserSeasonScoreTest.php b/tests/Models/UserSeasonScoreTest.php index 4276550be2a..35bb10e4493 100644 --- a/tests/Models/UserSeasonScoreTest.php +++ b/tests/Models/UserSeasonScoreTest.php @@ -22,7 +22,7 @@ class UserSeasonScoreTest extends TestCase public function testAddMultipleScores(): void { - $this->createRoomWithPlay(10); + $this->createRoomWithPlay(10, 'A'); $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) ->where('season_id', $this->season->getKey()) @@ -30,12 +30,12 @@ public function testAddMultipleScores(): void $this->assertSame($userScore->total_score, (float) 10); // 10*1 - $this->createRoomWithPlay(15); + $this->createRoomWithPlay(15, 'B'); $userScore->refresh(); $this->assertSame($userScore->total_score, 22.5); // 15*1 + 10*0.75 - $this->createRoomWithPlay(25); + $this->createRoomWithPlay(25, 'C'); $userScore->refresh(); $this->assertSame($userScore->total_score, 41.25); // 25*1 + 15*0.75 + 10*0.5 @@ -43,7 +43,7 @@ public function testAddMultipleScores(): void public function testAddMultipleScoresWithChildrenRooms(): void { - $firstRoom = $this->createRoomWithPlay(10); + $this->createRoomWithPlay(10, 'A'); $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) ->where('season_id', $this->season->getKey()) @@ -51,27 +51,27 @@ public function testAddMultipleScoresWithChildrenRooms(): void $this->assertSame($userScore->total_score, (float) 10); // 10*1 - $this->createRoomWithPlay(15, $firstRoom->getKey()); + $this->createRoomWithPlay(15, 'A'); $userScore->refresh(); $this->assertSame($userScore->total_score, (float) 15); // 15*1 - $secondRoom = $this->createRoomWithPlay(20); + $this->createRoomWithPlay(20, 'B'); $userScore->refresh(); $this->assertSame($userScore->total_score, 31.25); // 20*1 + 15*0.75 - $this->createRoomWithPlay(20, $secondRoom->getKey()); + $this->createRoomWithPlay(20, 'B'); $userScore->refresh(); $this->assertSame($userScore->total_score, 31.25); // 20*1 + 15*0.75 - $thirdRoom = $this->createRoomWithPlay(10); + $this->createRoomWithPlay(10, 'C'); $userScore->refresh(); $this->assertSame($userScore->total_score, 36.25); // 20*1 + 15*0.75 + 10*0.5 - $this->createRoomWithPlay(30, $thirdRoom->getKey()); + $this->createRoomWithPlay(30, 'C'); $userScore->refresh(); $this->assertSame($userScore->total_score, 52.5); // 30*1 + 20*0.75 + 15*0.5 @@ -79,7 +79,7 @@ public function testAddMultipleScoresWithChildrenRooms(): void public function testAddHigherScoreInChildRoom(): void { - $room = $this->createRoomWithPlay(10); + $this->createRoomWithPlay(10, 'A'); $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) ->where('season_id', $this->season->getKey()) @@ -87,7 +87,7 @@ public function testAddHigherScoreInChildRoom(): void $this->assertSame($userScore->total_score, (float) 10); - $this->createRoomWithPlay(15, $room->getKey()); + $this->createRoomWithPlay(15, 'A'); $userScore->refresh(); $this->assertSame($userScore->total_score, (float) 15); @@ -95,7 +95,7 @@ public function testAddHigherScoreInChildRoom(): void public function testAddHigherScoreInParentRoom(): void { - $room = $this->createRoomWithPlay(15); + $this->createRoomWithPlay(15, 'A'); $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) ->where('season_id', $this->season->getKey()) @@ -103,7 +103,7 @@ public function testAddHigherScoreInParentRoom(): void $this->assertSame($userScore->total_score, (float) 15); - $this->createRoomWithPlay(10, $room->getKey()); + $this->createRoomWithPlay(10, 'A'); $userScore->refresh(); $this->assertSame($userScore->total_score, (float) 15); @@ -111,7 +111,7 @@ public function testAddHigherScoreInParentRoom(): void public function testAddSameScoreInChildAndParentRoom(): void { - $room = $this->createRoomWithPlay(10); + $this->createRoomWithPlay(10, 'A'); $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) ->where('season_id', $this->season->getKey()) @@ -119,7 +119,7 @@ public function testAddSameScoreInChildAndParentRoom(): void $this->assertSame($userScore->total_score, (float) 10); - $this->createRoomWithPlay(10, $room->getKey()); + $this->createRoomWithPlay(10, 'A'); $userScore->refresh(); $this->assertSame($userScore->total_score, (float) 10); @@ -127,8 +127,20 @@ public function testAddSameScoreInChildAndParentRoom(): void public function testAddScoreInChildRoomOnly(): void { - $room = $this->createRoom(); - $this->createRoomWithPlay(10, $room->getKey()); + $this->createRoom('A'); + $this->createRoomWithPlay(10, 'A'); + + $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) + ->where('season_id', $this->season->getKey()) + ->first(); + + $this->assertSame($userScore->total_score, (float) 10); + } + + public function testAddScoreInSecondRoomOnly(): void + { + $this->createRoom('A'); + $this->createRoomWithPlay(10, 'B'); $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) ->where('season_id', $this->season->getKey()) @@ -147,14 +159,14 @@ protected function setUp(): void $this->user = User::factory()->create(); } - private function createRoom(?int $parentId = null): Room + private function createRoom(string $groupIndicator): Room { $room = Room::factory()->create([ 'category' => 'spotlight', - 'parent_id' => $parentId, ]); SeasonRoom::factory()->create([ + 'group_indicator' => $groupIndicator, 'room_id' => $room, 'season_id' => $this->season, ]); @@ -162,9 +174,9 @@ private function createRoom(?int $parentId = null): Room return $room; } - private function createRoomWithPlay(float $totalScore, ?int $parentId = null): Room + private function createRoomWithPlay(float $totalScore, string $groupIndicator): Room { - $room = $this->createRoom($parentId); + $room = $this->createRoom($groupIndicator); $playlistItem = PlaylistItem::factory()->create([ 'owner_id' => $room->host, From 086461f46fc577d9b29c3ea01c0a5e0acce6280b Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Sun, 12 Jan 2025 23:07:29 +0100 Subject: [PATCH 071/153] UserSeasonScore -> UserSeasonScoreAggregate --- app/Models/User.php | 2 +- ...nScore.php => UserSeasonScoreAggregate.php} | 2 +- ...ate_user_season_score_aggregates_table.php} | 2 +- ...st.php => UserSeasonScoreAggregateTest.php} | 18 +++++++++--------- 4 files changed, 12 insertions(+), 12 deletions(-) rename app/Models/{UserSeasonScore.php => UserSeasonScoreAggregate.php} (97%) rename database/migrations/{2024_10_22_190453_create_user_season_scores_table.php => 2024_10_22_190453_create_user_season_score_aggregates_table.php} (91%) rename tests/Models/{UserSeasonScoreTest.php => UserSeasonScoreAggregateTest.php} (87%) diff --git a/app/Models/User.php b/app/Models/User.php index fe8e03b33e9..9dbf4ed7af6 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -1361,7 +1361,7 @@ public function country() public function seasonScores(): HasMany { - return $this->hasMany(UserSeasonScore::class); + return $this->hasMany(UserSeasonScoreAggregate::class); } public function statisticsOsu() diff --git a/app/Models/UserSeasonScore.php b/app/Models/UserSeasonScoreAggregate.php similarity index 97% rename from app/Models/UserSeasonScore.php rename to app/Models/UserSeasonScoreAggregate.php index 7a2701c2c66..5e2739b0733 100644 --- a/app/Models/UserSeasonScore.php +++ b/app/Models/UserSeasonScoreAggregate.php @@ -16,7 +16,7 @@ * @property float $total_score * @property int $user_id */ -class UserSeasonScore extends Model +class UserSeasonScoreAggregate extends Model { public $incrementing = false; public $timestamps = false; diff --git a/database/migrations/2024_10_22_190453_create_user_season_scores_table.php b/database/migrations/2024_10_22_190453_create_user_season_score_aggregates_table.php similarity index 91% rename from database/migrations/2024_10_22_190453_create_user_season_scores_table.php rename to database/migrations/2024_10_22_190453_create_user_season_score_aggregates_table.php index cd7045105c5..9cd1ce910e4 100644 --- a/database/migrations/2024_10_22_190453_create_user_season_scores_table.php +++ b/database/migrations/2024_10_22_190453_create_user_season_score_aggregates_table.php @@ -16,7 +16,7 @@ */ public function up(): void { - Schema::create('user_season_scores', function (Blueprint $table) { + Schema::create('user_season_score_aggregates', function (Blueprint $table) { $table->bigInteger('user_id')->unsigned(); $table->integer('season_id')->unsigned(); $table->float('total_score'); diff --git a/tests/Models/UserSeasonScoreTest.php b/tests/Models/UserSeasonScoreAggregateTest.php similarity index 87% rename from tests/Models/UserSeasonScoreTest.php rename to tests/Models/UserSeasonScoreAggregateTest.php index 35bb10e4493..cbe9ca5937d 100644 --- a/tests/Models/UserSeasonScoreTest.php +++ b/tests/Models/UserSeasonScoreAggregateTest.php @@ -12,10 +12,10 @@ use App\Models\Season; use App\Models\SeasonRoom; use App\Models\User; -use App\Models\UserSeasonScore; +use App\Models\UserSeasonScoreAggregate; use Tests\TestCase; -class UserSeasonScoreTest extends TestCase +class UserSeasonScoreAggregateTest extends TestCase { private Season $season; private User $user; @@ -24,7 +24,7 @@ public function testAddMultipleScores(): void { $this->createRoomWithPlay(10, 'A'); - $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) + $userScore = UserSeasonScoreAggregate::where('user_id', $this->user->getKey()) ->where('season_id', $this->season->getKey()) ->first(); @@ -45,7 +45,7 @@ public function testAddMultipleScoresWithChildrenRooms(): void { $this->createRoomWithPlay(10, 'A'); - $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) + $userScore = UserSeasonScoreAggregate::where('user_id', $this->user->getKey()) ->where('season_id', $this->season->getKey()) ->first(); @@ -81,7 +81,7 @@ public function testAddHigherScoreInChildRoom(): void { $this->createRoomWithPlay(10, 'A'); - $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) + $userScore = UserSeasonScoreAggregate::where('user_id', $this->user->getKey()) ->where('season_id', $this->season->getKey()) ->first(); @@ -97,7 +97,7 @@ public function testAddHigherScoreInParentRoom(): void { $this->createRoomWithPlay(15, 'A'); - $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) + $userScore = UserSeasonScoreAggregate::where('user_id', $this->user->getKey()) ->where('season_id', $this->season->getKey()) ->first(); @@ -113,7 +113,7 @@ public function testAddSameScoreInChildAndParentRoom(): void { $this->createRoomWithPlay(10, 'A'); - $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) + $userScore = UserSeasonScoreAggregate::where('user_id', $this->user->getKey()) ->where('season_id', $this->season->getKey()) ->first(); @@ -130,7 +130,7 @@ public function testAddScoreInChildRoomOnly(): void $this->createRoom('A'); $this->createRoomWithPlay(10, 'A'); - $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) + $userScore = UserSeasonScoreAggregate::where('user_id', $this->user->getKey()) ->where('season_id', $this->season->getKey()) ->first(); @@ -142,7 +142,7 @@ public function testAddScoreInSecondRoomOnly(): void $this->createRoom('A'); $this->createRoomWithPlay(10, 'B'); - $userScore = UserSeasonScore::where('user_id', $this->user->getKey()) + $userScore = UserSeasonScoreAggregate::where('user_id', $this->user->getKey()) ->where('season_id', $this->season->getKey()) ->first(); From 68240dccfb0138b9a1d3c5b838cafa0e70604532 Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Sun, 12 Jan 2025 23:14:55 +0100 Subject: [PATCH 072/153] mute exception for Room::completePlay() usage --- app/Console/Commands/UserSeasonScoresRecalculate.php | 2 +- app/Models/UserSeasonScoreAggregate.php | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/Console/Commands/UserSeasonScoresRecalculate.php b/app/Console/Commands/UserSeasonScoresRecalculate.php index 7ed541c50b1..4629b5a6be4 100644 --- a/app/Console/Commands/UserSeasonScoresRecalculate.php +++ b/app/Console/Commands/UserSeasonScoresRecalculate.php @@ -50,7 +50,7 @@ protected function recalculate(Season $season): void ->firstOrNew(); $seasonScore->season_id = $season->getKey(); - $seasonScore->calculate(); + $seasonScore->calculate(false); $seasonScore->save(); $bar->advance(); diff --git a/app/Models/UserSeasonScoreAggregate.php b/app/Models/UserSeasonScoreAggregate.php index 5e2739b0733..bf8db9abfcc 100644 --- a/app/Models/UserSeasonScoreAggregate.php +++ b/app/Models/UserSeasonScoreAggregate.php @@ -24,7 +24,7 @@ class UserSeasonScoreAggregate extends Model protected $primaryKey = ':composite'; protected $primaryKeys = ['user_id', 'season_id']; - public function calculate(): void + public function calculate(bool $muteExceptions = true): void { $rooms = $this->season->rooms() ->withPivot('group_indicator') @@ -38,7 +38,12 @@ public function calculate(): void $roomsGrouped = $rooms->groupBy('pivot.group_indicator'); if ($roomsGrouped->count() > count($factors)) { - throw new InvariantException(osu_trans('rankings.seasons.validation.not_enough_factors')); + // don't interrupt Room::completePlay() and throw exception only for recalculation command + if ($muteExceptions) { + return; + } else { + throw new InvariantException(osu_trans('rankings.seasons.validation.not_enough_factors')); + } } foreach ($roomsGrouped as $rooms) { From 921b9892cbe2fbc092446f506a09667b6adc756d Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Sun, 12 Jan 2025 23:19:08 +0100 Subject: [PATCH 073/153] clean-up properties --- app/Models/Multiplayer/Room.php | 1 - app/Models/User.php | 1 + app/Models/UserSeasonScoreAggregate.php | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Models/Multiplayer/Room.php b/app/Models/Multiplayer/Room.php index 1009f6326ea..a6bc533c47d 100644 --- a/app/Models/Multiplayer/Room.php +++ b/app/Models/Multiplayer/Room.php @@ -37,7 +37,6 @@ * @property int $id * @property int|null $max_attempts * @property string $name - * @property int|null $parent_id * @property int $participant_count * @property \Illuminate\Database\Eloquent\Collection $playlist PlaylistItem * @property \Illuminate\Database\Eloquent\Collection $scoreLinks ScoreLink diff --git a/app/Models/User.php b/app/Models/User.php index 9dbf4ed7af6..0a40c4ef026 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -119,6 +119,7 @@ * @property-read Collection $scoresMania * @property-read Collection $scoresOsu * @property-read Collection $scoresTaiko + * @property-read Collection $seasonScores * @property-read UserStatistics\Fruits|null $statisticsFruits * @property-read UserStatistics\Mania|null $statisticsMania * @property-read UserStatistics\Mania4k|null $statisticsMania4k diff --git a/app/Models/UserSeasonScoreAggregate.php b/app/Models/UserSeasonScoreAggregate.php index bf8db9abfcc..a8052380029 100644 --- a/app/Models/UserSeasonScoreAggregate.php +++ b/app/Models/UserSeasonScoreAggregate.php @@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; /** + * @property-read Season $season * @property int $season_id * @property float $total_score * @property int $user_id From 66283ac440fe24b829fe84045e6ed5032b9cad76 Mon Sep 17 00:00:00 2001 From: Venix <30481900+venix12@users.noreply.github.com> Date: Mon, 13 Jan 2025 09:34:30 +0100 Subject: [PATCH 074/153] remove redundant function --- app/Models/Season.php | 8 -------- app/Models/UserSeasonScoreAggregate.php | 3 ++- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/app/Models/Season.php b/app/Models/Season.php index 9981d7e3f35..f3ce08685cf 100644 --- a/app/Models/Season.php +++ b/app/Models/Season.php @@ -52,14 +52,6 @@ public function endDate(): ?Carbon : null; } - public function scoreFactorsOrderedForCalculation(): array - { - $factors = $this->score_factors ?? []; - rsort($factors); - - return $factors; - } - public function startDate(): ?Carbon { return $this->rooms->min('starts_at'); diff --git a/app/Models/UserSeasonScoreAggregate.php b/app/Models/UserSeasonScoreAggregate.php index a8052380029..62bcd1cb96b 100644 --- a/app/Models/UserSeasonScoreAggregate.php +++ b/app/Models/UserSeasonScoreAggregate.php @@ -35,7 +35,7 @@ public function calculate(bool $muteExceptions = true): void ->where('user_id', $this->user_id) ->get(); - $factors = $this->season->scoreFactorsOrderedForCalculation(); + $factors = $this->season->score_factors; $roomsGrouped = $rooms->groupBy('pivot.group_indicator'); if ($roomsGrouped->count() > count($factors)) { @@ -59,6 +59,7 @@ public function calculate(bool $muteExceptions = true): void $scores[] = $groupUserScores->max(); } + rsort($factors); rsort($scores); $scoreCount = count($scores); From 73162ed460fa34e3bd06f6f628fc2384f6fa8fc2 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 14 Jan 2025 13:09:01 +0900 Subject: [PATCH 075/153] already a copy --- resources/js/beatmapsets-show/controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/js/beatmapsets-show/controller.ts b/resources/js/beatmapsets-show/controller.ts index a48de73ff41..b7651e70e9a 100644 --- a/resources/js/beatmapsets-show/controller.ts +++ b/resources/js/beatmapsets-show/controller.ts @@ -104,7 +104,7 @@ export default class Controller { const tag = asTagJsonWithCount(maybeTag); tag.count = tagId.count; - userTags.push(asTagJsonWithCount(tag)); + userTags.push(tag); } } From 585fa90fbf894ef3c2898f8a1cee4e99e0fd42af Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 14 Jan 2025 14:40:27 +0900 Subject: [PATCH 076/153] tags not being deduped anymore --- resources/js/beatmapsets-show/info.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/js/beatmapsets-show/info.tsx b/resources/js/beatmapsets-show/info.tsx index b5e3eadb3e1..dcbb8e30f3d 100644 --- a/resources/js/beatmapsets-show/info.tsx +++ b/resources/js/beatmapsets-show/info.tsx @@ -202,8 +202,8 @@ export default class Info extends React.Component { {trans('beatmapsets.show.info.tags')}
- {this.tags.map((tag) => ( - + {this.tags.map((tag, i) => ( + Date: Tue, 14 Jan 2025 15:28:34 +0900 Subject: [PATCH 077/153] fix property ordering --- app/Models/BeatmapTag.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Models/BeatmapTag.php b/app/Models/BeatmapTag.php index fc7abf63ce8..c83f36a0f8d 100644 --- a/app/Models/BeatmapTag.php +++ b/app/Models/BeatmapTag.php @@ -18,9 +18,10 @@ */ class BeatmapTag extends Model { + public $incrementing = false; + protected $primaryKey = ':composite'; protected $primaryKeys = ['beatmap_id', 'tag_id', 'user_id']; - public $incrementing = false; public static function topTagIdsQuery(int $beatmapId, int $limit = 50) { From be4740055755b2ccca8440b7fbeea498ba9eb245 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 14 Jan 2025 15:31:19 +0900 Subject: [PATCH 078/153] only used in one place now... --- resources/js/beatmapsets-show/controller.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/resources/js/beatmapsets-show/controller.ts b/resources/js/beatmapsets-show/controller.ts index b7651e70e9a..8216dd4c523 100644 --- a/resources/js/beatmapsets-show/controller.ts +++ b/resources/js/beatmapsets-show/controller.ts @@ -27,14 +27,6 @@ interface State { type TagJsonWithCount = TagJson & { count: number }; -function asTagJsonWithCount(tag: TagJson) { - return { - count: 0, - ...tag, - }; -} - - export default class Controller { @observable hoveredBeatmap: null | BeatmapJsonForBeatmapsetShow = null; @observable state: State; @@ -102,9 +94,7 @@ export default class Controller { const maybeTag = this.relatedTags.get(tagId.tag_id); if (maybeTag == null) continue; - const tag = asTagJsonWithCount(maybeTag); - tag.count = tagId.count; - userTags.push(tag); + userTags.push({ ...maybeTag, count: tagId.count } ); } } From 3dee3fcb29c85ddc9ab4e77374640785ec6a5ada Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 14 Jan 2025 16:46:40 +0900 Subject: [PATCH 079/153] can be a scope? --- app/Models/Beatmap.php | 2 +- app/Models/BeatmapTag.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Models/Beatmap.php b/app/Models/Beatmap.php index 3cca72263e3..9bda60e088d 100644 --- a/app/Models/Beatmap.php +++ b/app/Models/Beatmap.php @@ -358,7 +358,7 @@ public function topTagIds() "beatmap_top_tag_ids:{$this->getKey()}", $GLOBALS['cfg']['osu']['tags']['beatmap_tags_cache_duration'], [], - fn () => BeatmapTag::topTagIdsQuery($this->getKey())->get()->toArray(), + fn () => $this->beatmapTags()->topTagIds()->limit(50)->get()->toArray(), ), ); } diff --git a/app/Models/BeatmapTag.php b/app/Models/BeatmapTag.php index c83f36a0f8d..a61da0fc571 100644 --- a/app/Models/BeatmapTag.php +++ b/app/Models/BeatmapTag.php @@ -7,6 +7,8 @@ namespace App\Models; +use Illuminate\Contracts\Database\Eloquent\Builder; + /** * @property-read Beatmap $beatmap * @property int $beatmap_id @@ -23,16 +25,14 @@ class BeatmapTag extends Model protected $primaryKey = ':composite'; protected $primaryKeys = ['beatmap_id', 'tag_id', 'user_id']; - public static function topTagIdsQuery(int $beatmapId, int $limit = 50) + public function scopeTopTagIds(Builder $query) { - return static::where('beatmap_id', $beatmapId) - ->whereHas('user', fn ($userQuery) => $userQuery->default()) + return $query->whereHas('user', fn ($userQuery) => $userQuery->default()) ->groupBy('tag_id') ->select('tag_id') ->selectRaw('COUNT(*) as count') ->orderBy('count', 'desc') - ->orderBy('tag_id', 'asc') - ->limit($limit); + ->orderBy('tag_id', 'asc'); } public function beatmap() From 12fa440666e7e75d92f1a39319f0a4372259afb2 Mon Sep 17 00:00:00 2001 From: AJ Granowski Date: Wed, 15 Jan 2025 03:17:07 -0600 Subject: [PATCH 080/153] Have octane wait for a manifest to exist PR: #11779 --- docker/development/run.sh | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docker/development/run.sh b/docker/development/run.sh index 372b5991eb7..4fd8d6eadc5 100755 --- a/docker/development/run.sh +++ b/docker/development/run.sh @@ -20,7 +20,19 @@ _migrate() { } _octane() { - exec ./artisan octane:start --host=0.0.0.0 "$@" + echo_counter=0 + manifest_path='./public/assets/manifest.json' + while [ ! -f "$manifest_path" ]; do + if [ "$echo_counter" -le 0 ]; then + echo_counter=5 + echo "waiting to start octane: ${manifest_path##*/} not found" >&2 + fi + + : $(( echo_counter -= 1 )) + sleep 1 + done + + exec ./artisan octane:start --host=0.0.0.0 "$@" } _schedule() { From 87db8f489caf8e2f153d08f7a68d2f8cb3f862f1 Mon Sep 17 00:00:00 2001 From: nanaya Date: Wed, 15 Jan 2025 21:29:32 +0900 Subject: [PATCH 081/153] Readd beatmap_id validation Ruleset id validation is done in ScoreToken. --- app/Models/Multiplayer/Room.php | 37 ++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/app/Models/Multiplayer/Room.php b/app/Models/Multiplayer/Room.php index 30041b14c4b..8dcf6d088ed 100644 --- a/app/Models/Multiplayer/Room.php +++ b/app/Models/Multiplayer/Room.php @@ -662,9 +662,21 @@ public function startPlay(User $user, PlaylistItem $playlistItem, array $rawPara { priv_check_user($user, 'MultiplayerScoreSubmit', $this)->ensureCan(); - $this->assertValidStartPlay($user, $playlistItem); + $params = get_params($rawParams, null, [ + 'beatmap_hash', + 'beatmap_id:int', + 'build_id', + 'ruleset_id:int', + ], ['null_missing' => true]); + + if (!$playlistItem->freestyle) { + $params['beatmap_id'] = $playlistItem->beatmap_id; + $params['ruleset_id'] = $playlistItem->ruleset_id; + } - return $this->getConnection()->transaction(function () use ($playlistItem, $rawParams, $user) { + $this->assertValidStartPlay($user, $playlistItem, $params); + + return $this->getConnection()->transaction(function () use ($params, $playlistItem, $user) { $agg = UserScoreAggregate::new($user, $this); if ($agg->wasRecentlyCreated) { $this->incrementInstance('participant_count'); @@ -676,11 +688,11 @@ public function startPlay(User $user, PlaylistItem $playlistItem, array $rawPara $playlistItemAgg->updateUserAttempts(); return ScoreToken::create([ - 'beatmap_hash' => get_string($rawParams['beatmap_hash'] ?? null), - 'beatmap_id' => $playlistItem->beatmap_id, - 'build_id' => $rawParams['build_id'], + 'beatmap_hash' => $params['beatmap_hash'], + 'beatmap_id' => $params['beatmap_id'], + 'build_id' => $params['build_id'], 'playlist_item_id' => $playlistItem->getKey(), - 'ruleset_id' => $playlistItem->ruleset_id, + 'ruleset_id' => $params['ruleset_id'], 'user_id' => $user->getKey(), ]); }); @@ -741,7 +753,7 @@ private function assertValidStartGame() } } - private function assertValidStartPlay(User $user, PlaylistItem $playlistItem) + private function assertValidStartPlay(User $user, PlaylistItem $playlistItem, array $params): void { // todo: check against room's end time (to see if player has enough time to play this beatmap) and is under the room's max attempts limit @@ -749,6 +761,17 @@ private function assertValidStartPlay(User $user, PlaylistItem $playlistItem) throw new InvariantException('Room has already ended.'); } + if ($playlistItem->freestyle) { + // assert the beatmap_id is part of playlist item's beatmapset + $beatmapsetIdCount = Beatmap + ::whereKey([$playlistItem->beatmap_id, $params['beatmap_id']]) + ->distinct('beatmapset_id') + ->count(); + if ($beatmapsetIdCount !== 1) { + throw new InvariantException('Specified beatmap_id is not allowed'); + } + } + $userId = $user->getKey(); if ($this->max_attempts !== null) { $roomStats = $this->userHighScores()->where('user_id', $userId)->first(); From 0d393a8e1f343eeea4abc457b8eddc8d91d10840 Mon Sep 17 00:00:00 2001 From: nanaya Date: Wed, 15 Jan 2025 21:34:52 +0900 Subject: [PATCH 082/153] Disallow mod on freestyle --- app/Models/Multiplayer/PlaylistItem.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Models/Multiplayer/PlaylistItem.php b/app/Models/Multiplayer/PlaylistItem.php index b8ab7d8b56b..37ca9b1a653 100644 --- a/app/Models/Multiplayer/PlaylistItem.php +++ b/app/Models/Multiplayer/PlaylistItem.php @@ -171,6 +171,10 @@ private function assertValidRuleset() private function assertValidMods() { + if ($this->freestyle && (count($this->allowed_mods) === 0 || count($this->required_mods) === 0)) { + throw new InvariantException("mod isn't allowed in freestyle"); + } + $allowedModIds = array_column($this->allowed_mods, 'acronym'); $requiredModIds = array_column($this->required_mods, 'acronym'); From adecc03f8e59ef424221b9b6f31bd1c52943385e Mon Sep 17 00:00:00 2001 From: nanaya Date: Wed, 15 Jan 2025 21:37:44 +0900 Subject: [PATCH 083/153] Lint fix --- .../Controllers/Multiplayer/Rooms/Playlist/ScoresController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php b/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php index 9dd6d4ce9ea..19b79dde254 100644 --- a/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php +++ b/app/Http/Controllers/Multiplayer/Rooms/Playlist/ScoresController.php @@ -7,7 +7,6 @@ use App\Http\Controllers\Controller as BaseController; use App\Libraries\ClientCheck; -use App\Models\Beatmap; use App\Models\Multiplayer\PlaylistItem; use App\Models\Multiplayer\PlaylistItemUserHighScore; use App\Models\Multiplayer\Room; From ac1b77b7ee6ef2ddc2be44aab23574e26b30d83e Mon Sep 17 00:00:00 2001 From: nanaya Date: Wed, 15 Jan 2025 21:42:04 +0900 Subject: [PATCH 084/153] No smarts allowed Easier to understand. --- app/Models/Multiplayer/Room.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/Models/Multiplayer/Room.php b/app/Models/Multiplayer/Room.php index 8dcf6d088ed..19e9d2c7d8b 100644 --- a/app/Models/Multiplayer/Room.php +++ b/app/Models/Multiplayer/Room.php @@ -763,11 +763,7 @@ private function assertValidStartPlay(User $user, PlaylistItem $playlistItem, ar if ($playlistItem->freestyle) { // assert the beatmap_id is part of playlist item's beatmapset - $beatmapsetIdCount = Beatmap - ::whereKey([$playlistItem->beatmap_id, $params['beatmap_id']]) - ->distinct('beatmapset_id') - ->count(); - if ($beatmapsetIdCount !== 1) { + if ($playlistItem->beatmap->beatmapset_id !== Beatmap::find($params['beatmap_id'])?->getKey()) { throw new InvariantException('Specified beatmap_id is not allowed'); } } From e69ab6ad6c50f3eafeecc9dcb50c296e76b50c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 15 Jan 2025 14:47:25 +0100 Subject: [PATCH 085/153] Apply review suggestions --- resources/css/bem/daily-challenge.less | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/resources/css/bem/daily-challenge.less b/resources/css/bem/daily-challenge.less index ecee003191d..0f95ff5d90a 100644 --- a/resources/css/bem/daily-challenge.less +++ b/resources/css/bem/daily-challenge.less @@ -8,10 +8,11 @@ position: relative; display: flex; align-items: center; - padding: 3px; + padding: 1px; + border: 2px solid transparent; &--played-today { - border: 2px solid @osu-colour-lime-1; + border-color: @osu-colour-lime-1; &::before { .fas(); @@ -19,10 +20,11 @@ border-radius: 50%; color: @osu-colour-lime-1; content: @fa-var-check-circle; - font-size: 16px; + font-size: 16px; // icon size position: absolute; - right: -8px; - top: -8px; + right: 0; + top: 0; + transform: translate(50%, -50%); } } From 2b05d0980cd8ce3655ebd41c9c2624425f5ad479 Mon Sep 17 00:00:00 2001 From: nanaya Date: Thu, 16 Jan 2025 15:34:58 +0900 Subject: [PATCH 086/153] Fix incorrect check and short circuit if passed --- app/Models/Multiplayer/PlaylistItem.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/Models/Multiplayer/PlaylistItem.php b/app/Models/Multiplayer/PlaylistItem.php index 37ca9b1a653..4751a5eeff6 100644 --- a/app/Models/Multiplayer/PlaylistItem.php +++ b/app/Models/Multiplayer/PlaylistItem.php @@ -171,8 +171,11 @@ private function assertValidRuleset() private function assertValidMods() { - if ($this->freestyle && (count($this->allowed_mods) === 0 || count($this->required_mods) === 0)) { - throw new InvariantException("mod isn't allowed in freestyle"); + if ($this->freestyle) { + if (count($this->allowed_mods) !== 0 || count($this->required_mods) !== 0) { + throw new InvariantException("mod isn't allowed in freestyle"); + } + return; } $allowedModIds = array_column($this->allowed_mods, 'acronym'); From 6f053b369d2ba5eeb3b7cf699b3a617bbe242c15 Mon Sep 17 00:00:00 2001 From: nanaya Date: Thu, 16 Jan 2025 15:48:43 +0900 Subject: [PATCH 087/153] Wrong id --- app/Models/Multiplayer/Room.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Multiplayer/Room.php b/app/Models/Multiplayer/Room.php index 19e9d2c7d8b..06088752613 100644 --- a/app/Models/Multiplayer/Room.php +++ b/app/Models/Multiplayer/Room.php @@ -763,7 +763,7 @@ private function assertValidStartPlay(User $user, PlaylistItem $playlistItem, ar if ($playlistItem->freestyle) { // assert the beatmap_id is part of playlist item's beatmapset - if ($playlistItem->beatmap->beatmapset_id !== Beatmap::find($params['beatmap_id'])?->getKey()) { + if ($playlistItem->beatmap->beatmapset_id !== Beatmap::find($params['beatmap_id'])?->beatmapset_id) { throw new InvariantException('Specified beatmap_id is not allowed'); } } From 954dc28bd74da48b9b7affe0becaa19a854576ec Mon Sep 17 00:00:00 2001 From: bakaneko Date: Thu, 16 Jan 2025 18:26:05 +0900 Subject: [PATCH 088/153] allow selectively excluding bots and no_profile users from lookup --- .../Controllers/Users/LookupController.php | 18 +++++++++++++----- .../Beatmapset/ChangeBeatmapOwners.php | 2 +- app/Models/User.php | 9 +++++---- .../beatmap-owner-editor.tsx | 1 + resources/js/chat/create-announcement.tsx | 1 + resources/js/components/username-input.tsx | 3 ++- resources/js/store/store-supporter-tag.tsx | 2 +- resources/js/utils/user.ts | 4 ++-- 8 files changed, 26 insertions(+), 14 deletions(-) diff --git a/app/Http/Controllers/Users/LookupController.php b/app/Http/Controllers/Users/LookupController.php index 725373624c2..f94f37bd8b0 100644 --- a/app/Http/Controllers/Users/LookupController.php +++ b/app/Http/Controllers/Users/LookupController.php @@ -22,7 +22,12 @@ public function __construct() public function index() { // TODO: referer check? - $ids = array_slice(array_reject_null(get_arr(request('ids'), presence(...)) ?? []), 0, 50); + $params = get_params(request()->all(), null, [ + 'exclude_bots:bool', + 'ids:string[]', + ]); + + $ids = array_slice(array_reject_null(get_arr($params['ids'] ?? [], presence(...))), 0, 50); $numericIds = []; $stringIds = []; @@ -35,12 +40,15 @@ public function index() } $users = User::where(fn ($q) => $q->whereIn('user_id', $numericIds)->orWhereIn('username', $stringIds)) - ->defaultForLookup() - ->with(UserCompactTransformer::CARD_INCLUDES_PRELOAD) - ->get(); + ->default() + ->with(UserCompactTransformer::CARD_INCLUDES_PRELOAD); + + if ($params['exclude_bots'] ?? false) { + $users = $users->withoutBots(); + } return [ - 'users' => json_collection($users, new UserCompactTransformer(), UserCompactTransformer::CARD_INCLUDES), + 'users' => json_collection($users->get(), new UserCompactTransformer(), UserCompactTransformer::CARD_INCLUDES), ]; } } diff --git a/app/Libraries/Beatmapset/ChangeBeatmapOwners.php b/app/Libraries/Beatmapset/ChangeBeatmapOwners.php index 5b137b9a60d..32ec991b00f 100644 --- a/app/Libraries/Beatmapset/ChangeBeatmapOwners.php +++ b/app/Libraries/Beatmapset/ChangeBeatmapOwners.php @@ -43,7 +43,7 @@ public function handle(): void $newUserIds = $this->userIds->diff($currentOwners); - if (User::whereIn('user_id', $newUserIds->toArray())->defaultForLookup()->count() !== $newUserIds->count()) { + if (User::whereIn('user_id', $newUserIds->toArray())->default()->withoutBots()->count() !== $newUserIds->count()) { throw new InvariantException('invalid user_id'); } diff --git a/app/Models/User.php b/app/Models/User.php index 7c951737fad..916f1f624fe 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2032,13 +2032,14 @@ public function scopeDefault($query) ]); } - public function scopeDefaultForLookup(Builder $query): Builder + public function scopeWithoutBots(Builder $query): Builder { $groups = app('groups'); - return $query - ->whereNotIn('group_id', [$groups->byIdentifier('no_profile')->getKey(), $groups->byIdentifier('bot')->getKey()]) - ->default(); + return $query->whereNotIn( + 'group_id', + [$groups->byIdentifier('no_profile')->getKey(), $groups->byIdentifier('bot')->getKey()], + ); } public function scopeOnline($query) diff --git a/resources/js/beatmap-discussions/beatmap-owner-editor.tsx b/resources/js/beatmap-discussions/beatmap-owner-editor.tsx index d071c24c6c3..f6d616000f1 100644 --- a/resources/js/beatmap-discussions/beatmap-owner-editor.tsx +++ b/resources/js/beatmap-discussions/beatmap-owner-editor.tsx @@ -189,6 +189,7 @@ export default class BeatmapOwnerEditor extends React.Component {
{this.editing ? ( {
{ } try { - this.xhr = apiLookupUsers(userIds); + this.xhr = apiLookupUsers(userIds, this.props.excludeBots); const response = await this.xhr; this.extractValidUsers(response.users); } catch (error) { diff --git a/resources/js/store/store-supporter-tag.tsx b/resources/js/store/store-supporter-tag.tsx index 5e39f229e90..4db15f63c38 100644 --- a/resources/js/store/store-supporter-tag.tsx +++ b/resources/js/store/store-supporter-tag.tsx @@ -224,7 +224,7 @@ export default class StoreSupporterTag extends React.Component { @action private readonly getUser = (username: string) => { - this.xhr = apiLookupUsers([`@${username}`]); + this.xhr = apiLookupUsers([`@${username}`], true); this.xhr .done((response) => runInAction(() => { diff --git a/resources/js/utils/user.ts b/resources/js/utils/user.ts index a403709d51f..1345ac81dee 100644 --- a/resources/js/utils/user.ts +++ b/resources/js/utils/user.ts @@ -4,9 +4,9 @@ import UserJson from 'interfaces/user-json'; import { route } from 'laroute'; -export function apiLookupUsers(idsOrUsernames: (string | null | undefined)[]) { +export function apiLookupUsers(idsOrUsernames: (string | null | undefined)[], excludeBots?: boolean) { return $.ajax(route('users.lookup'), { - data: { ids: idsOrUsernames }, + data: { exclude_bots: excludeBots, ids: idsOrUsernames }, dataType: 'json', }) as JQuery.jqXHR<{ users: UserJson[] }>; } From fd749b56ddd9428d2e8f56f148eb956e75cb2e79 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Thu, 16 Jan 2025 19:49:46 +0900 Subject: [PATCH 089/153] use Request facade instead --- app/Http/Controllers/Users/LookupController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Users/LookupController.php b/app/Http/Controllers/Users/LookupController.php index f94f37bd8b0..0b22521f6ca 100644 --- a/app/Http/Controllers/Users/LookupController.php +++ b/app/Http/Controllers/Users/LookupController.php @@ -22,7 +22,7 @@ public function __construct() public function index() { // TODO: referer check? - $params = get_params(request()->all(), null, [ + $params = get_params(\Request::all(), null, [ 'exclude_bots:bool', 'ids:string[]', ]); From 82fc1122df27ef1b39d19375f44c71fe049883a7 Mon Sep 17 00:00:00 2001 From: nanaya Date: Thu, 16 Jan 2025 19:55:18 +0900 Subject: [PATCH 090/153] Remove proxying option of blade background image --- app/helpers.php | 12 ++++-------- resources/views/follows/modding.blade.php | 2 +- resources/views/forum/topics/_post_info.blade.php | 2 +- resources/views/layout/_header_user.blade.php | 2 +- resources/views/layout/_page_header_v4.blade.php | 2 +- resources/views/layout/_popup_user.blade.php | 2 +- resources/views/objects/_flag_team.blade.php | 2 +- 7 files changed, 10 insertions(+), 14 deletions(-) diff --git a/app/helpers.php b/app/helpers.php index 81685d9c2bc..b375c176c4c 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -55,15 +55,11 @@ function atom_id(string $namespace, $id = null): string return 'tag:'.request()->getHttpHost().',2019:'.$namespace.($id === null ? '' : "/{$id}"); } -function background_image($url, $proxy = true) +function background_image($url): string { - if (!present($url)) { - return ''; - } - - $url = $proxy ? proxy_media($url) : $url; - - return sprintf(' style="background-image:url(\'%s\');" ', e($url)); + return present($url) + ? sprintf(' style="background-image:url(\'%s\');" ', e($url)) + : ''; } function beatmap_timestamp_format($ms) diff --git a/resources/views/follows/modding.blade.php b/resources/views/follows/modding.blade.php index 18f677dec3b..210c646fdc2 100644 --- a/resources/views/follows/modding.blade.php +++ b/resources/views/follows/modding.blade.php @@ -60,7 +60,7 @@
beatmapset->coverURL('list'), false) !!} + {!! background_image($watch->beatmapset->coverURL('list')) !!} class="beatmapset-watches__cover" >
diff --git a/resources/views/forum/topics/_post_info.blade.php b/resources/views/forum/topics/_post_info.blade.php index db4bd562cbd..0b2cc37e46e 100644 --- a/resources/views/forum/topics/_post_info.blade.php +++ b/resources/views/forum/topics/_post_info.blade.php @@ -76,7 +76,7 @@ class="forum-post-info__row forum-post-info__row--title" logo()->url(), false) !!} + {!! background_image($team->logo()->url()) !!} >
diff --git a/resources/views/layout/_header_user.blade.php b/resources/views/layout/_header_user.blade.php index 1905100230f..7e7d2338157 100644 --- a/resources/views/layout/_header_user.blade.php +++ b/resources/views/layout/_header_user.blade.php @@ -21,6 +21,6 @@ class="{{ $class }} avatar--guest" class="{{ $class }} {{ Auth::user()->isRestricted() ? 'avatar--restricted' : '' }}" data-click-menu-target="nav2-user-popup" href="{{ route('users.show', Auth::user()) }}" - {!! background_image(Auth::user()->user_avatar, false) !!} + {!! background_image(Auth::user()->user_avatar) !!} > @endif diff --git a/resources/views/layout/_page_header_v4.blade.php b/resources/views/layout/_page_header_v4.blade.php index 963d1093f22..36d78c726da 100644 --- a/resources/views/layout/_page_header_v4.blade.php +++ b/resources/views/layout/_page_header_v4.blade.php @@ -24,7 +24,7 @@ ">
-
+