From 6eee4ea0a5be5ebaff4e6f499da70282df24d89e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 07:04:35 +0000 Subject: [PATCH 1/3] Initial plan From 3da2cc9d32bc94b4d28822a4863d1cf284640d5d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 07:08:04 +0000 Subject: [PATCH 2/3] chore: initial plan for QA-02 typescript strict mode fixes Co-authored-by: paruff <3465944+paruff@users.noreply.github.com> --- package-lock.json | 55 +++++++++++++++++++++++++++++++---------------- package.json | 7 +++--- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index e892e8b..d586124 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,7 +74,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -528,6 +527,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" @@ -544,6 +544,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -559,6 +560,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -574,6 +576,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", @@ -591,6 +594,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/traverse": "^7.28.6" @@ -691,6 +695,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" }, @@ -811,6 +816,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -997,6 +1003,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -1062,6 +1069,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1108,6 +1116,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6" @@ -1176,6 +1185,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" @@ -1192,6 +1202,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1207,6 +1218,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" @@ -1223,6 +1235,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1238,6 +1251,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/plugin-transform-destructuring": "^7.28.5" @@ -1254,6 +1268,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -1333,6 +1348,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -1378,6 +1394,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1393,6 +1410,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -1425,6 +1443,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz", "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", @@ -1443,6 +1462,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -1475,6 +1495,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1539,6 +1560,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1" @@ -1634,6 +1656,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1759,6 +1782,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" @@ -1775,6 +1799,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1865,6 +1890,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1880,6 +1906,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1914,6 +1941,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1929,6 +1957,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" @@ -1961,6 +1990,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" @@ -2062,6 +2092,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.1.tgz", "integrity": "sha512-ENp89vM9Pw4kv/koBb5N2f9bDZsR0hpf3BdPMOg/pkS3pwO4dzNnQZVXtBbeyAadgm865DmQG2jMMLqmZXvuCw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.7", "core-js-compat": "^3.48.0" @@ -2075,6 +2106,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -2101,6 +2133,7 @@ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -2307,7 +2340,6 @@ "integrity": "sha512-7A/9CJpJDxv1SQ7hAZU0zPn2yRxx6XMR+LO4T94Enm3cYNWsEEj+RGX38NLX4INT+H6w5raX3Csb/qs4vUBsOA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@cucumber/ci-environment": "13.0.0", "@cucumber/cucumber-expressions": "19.0.0", @@ -2460,7 +2492,6 @@ "integrity": "sha512-duEXK+KDfQUzu3vsSzXjkxQ2tirF5PRsc1Xrts6THKHJO6mjw4RjM8RV+vliuDasmhhrmdLcOcM7d9nurNTJKw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@cucumber/messages": ">=31.0.0 <33" } @@ -2553,7 +2584,6 @@ "integrity": "sha512-Kxap9uP5jD8tHUZVjTWgzxemi/0uOsbGjd4LBOSxcJoOCRbESFwemUzilJuzNTB8pcTQUh8D5oudUyxfkJOKmA==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "@cucumber/messages": ">=17.1.1" } @@ -2564,7 +2594,6 @@ "integrity": "sha512-1OSoW+GQvFUNAl6tdP2CTBexTXMNJF0094goVUcvugtQeXtJ0K8sCP0xbq7GGoiezs/eJAAOD03+zAPT64orHQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "class-transformer": "0.5.1", "reflect-metadata": "0.2.2" @@ -3452,7 +3481,6 @@ "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-4.0.1.tgz", "integrity": "sha512-CRpbLvdJ1T42S+lrYa1iZp1KfDeBp4oeZOK3hdpiS5n0vR0nhD6sC1gGF0sTboCTp64tLteikz5Y3j53dvgOIw==", "license": "MIT", - "peer": true, "peerDependencies": { "react-native": "*" } @@ -3746,7 +3774,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.2.tgz", "integrity": "sha512-jwtMmJa1BXXDCiDx1vC6SFN/+HfYG53UkfJa6qeN5ogvOunzbFDO3wISZy5n9xgYFUrEP6M7e8EG++riHNTv9w==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", @@ -3813,7 +3840,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.2.tgz", "integrity": "sha512-LssbyKHlwLeiV8GBATyOyjmHcMpX/tFjzRUCS1jnwGAew1VsBB4fJowyS5Ud5LdFbYpJeS+IQoC+RQxpK7eH3Q==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@firebase/app": "0.13.2", "@firebase/component": "0.6.18", @@ -3829,8 +3855,7 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/@firebase/auth": { "version": "1.10.8", @@ -4281,7 +4306,6 @@ "integrity": "sha512-zGlBn/9Dnya5ta9bX/fgEoNC3Cp8s6h+uYPYaDieZsFOAdHP/ExzQ/eaDgxD3GOROdPkLKpvKY0iIzr9adle0w==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" }, @@ -5528,7 +5552,6 @@ "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.18.tgz", "integrity": "sha512-mIT9MiL/vMm4eirLcmw2h6h/Nm5FICtnYSdohq4vTLA2FF/6PNhByM7s8ffqoVfE5L0uAa6Xda1B7oddolUiGg==", "license": "MIT", - "peer": true, "dependencies": { "@react-navigation/core": "^6.4.17", "escape-string-regexp": "^4.0.0", @@ -5772,7 +5795,6 @@ "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -6517,7 +6539,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -7740,6 +7761,7 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -7867,7 +7889,6 @@ "resolved": "https://registry.npmjs.org/expo/-/expo-52.0.49.tgz", "integrity": "sha512-ge3gUnuyGEePWWKzPY7TQ7FsvtFTdmsdYDHeBVUjMr9KIoQig/gf8A03oH26p3UtTL6sUJcyOIg9vwIHGNPSUw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "0.22.28", @@ -12537,7 +12558,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -12621,7 +12641,6 @@ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.76.5.tgz", "integrity": "sha512-op2p2kB+lqMF1D7AdX4+wvaR0OPFbvWYs+VBE7bwsb99Cn9xISrLRLAgFflZedQsa5HvnOGrULhtnmItbIKVVw==", "license": "MIT", - "peer": true, "dependencies": { "@jest/create-cache-key-function": "^29.6.3", "@react-native/assets-registry": "0.76.5", @@ -12699,7 +12718,6 @@ "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.14.1.tgz", "integrity": "sha512-+tUhT5WBl8nh5+P+chYhAjR470iCByf9z5EYdCEbPaAK3Yfzw+o8VRPnUgmPAKlSccOgQBxx3NOl/Wzckn9ujg==", "license": "MIT", - "peer": true, "peerDependencies": { "react": "*", "react-native": "*" @@ -12710,7 +12728,6 @@ "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.37.0.tgz", "integrity": "sha512-vEi4qZqWYoGuVGuHTv1K2XA90rgSydksmR5+tb5uhL93whl6Bch6EEXzC+8eEfj4SimiCgXBPY7r/xTXJxvnUg==", "license": "MIT", - "peer": true, "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" diff --git a/package.json b/package.json index 8c4b111..1bb8270 100644 --- a/package.json +++ b/package.json @@ -39,17 +39,16 @@ "react-native-web": "~0.19.13" }, "devDependencies": { - "@types/react": "~18.3.12", - "typescript": "~5.3.3", "@cucumber/cucumber": "^12.7.0", + "@types/react": "~18.3.12", "cross-env": "^7.0.3", "firebase-admin": "^12.6.0", "playwright": "^1.52.0", - "tsx": "^4.20.6" + "tsx": "^4.20.6", + "typescript": "~5.3.3" }, "overrides": { "tar": ">=7.5.10", "@xmldom/xmldom": ">=0.8.0" } } - From 154df7e6213147d58daebf95335182cb639c335d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 07:12:08 +0000 Subject: [PATCH 3/3] feat(QA-02): enable TypeScript strict compliance across codebase Co-authored-by: paruff <3465944+paruff@users.noreply.github.com> --- .github/workflows/e2e-bdd.yml | 3 +++ package.json | 1 + src/components/MilestoneAnimation.tsx | 2 +- src/screens/auth/LoginScreen.tsx | 9 ++++---- src/screens/auth/RegisterScreen.tsx | 5 +++-- src/screens/goals/CreateGoalScreen.tsx | 7 +++--- src/screens/goals/DepositScreen.tsx | 7 +++--- src/screens/goals/EditGoalScreen.tsx | 13 +++++------ src/services/goalService.ts | 18 ++++++++++++++-- src/utils/errorUtils.ts | 7 ++++++ tests/errorUtils.test.ts | 30 ++++++++++++++++++++++++++ 11 files changed, 81 insertions(+), 21 deletions(-) create mode 100644 src/utils/errorUtils.ts create mode 100644 tests/errorUtils.test.ts diff --git a/.github/workflows/e2e-bdd.yml b/.github/workflows/e2e-bdd.yml index b82b7f4..af2f86b 100644 --- a/.github/workflows/e2e-bdd.yml +++ b/.github/workflows/e2e-bdd.yml @@ -22,6 +22,9 @@ jobs: - name: Install dependencies run: npm ci + - name: Type check + run: npm run typecheck + - name: Install Playwright browser run: npx playwright install --with-deps chromium diff --git a/package.json b/package.json index 1bb8270..8638a1c 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "test:e2e:report": "cucumber-js --config cucumber.js --format progress-bar --format html:reports/cucumber-report.html", "test:e2e:full": "cross-env EXPO_PUBLIC_E2E_MODE=true BASE_URL=http://localhost:3000 cucumber-js --config cucumber.js", "test:e2e:ci": "cross-env EXPO_PUBLIC_E2E_MODE=true BASE_URL=http://localhost:3000 cucumber-js --config cucumber.js --format progress-bar --format html:reports/cucumber-report.html --format json:reports/cucumber-report.json", + "typecheck": "tsc --noEmit", "test:unit": "node --import tsx --test tests/**/*.test.ts", "admin:support": "node scripts/admin-support.mjs" }, diff --git a/src/components/MilestoneAnimation.tsx b/src/components/MilestoneAnimation.tsx index 53ee3f3..5066095 100644 --- a/src/components/MilestoneAnimation.tsx +++ b/src/components/MilestoneAnimation.tsx @@ -206,7 +206,7 @@ export default function MilestoneAnimation({ {Math.round(safeProgress * 100)}% visible Stage {progressStage + 1} of 11 {theme === 'house' && ( - Land -> Foundation -> Walls -> Roof -> Landscaping + {'Land -> Foundation -> Walls -> Roof -> Landscaping'} )} )} diff --git a/src/screens/auth/LoginScreen.tsx b/src/screens/auth/LoginScreen.tsx index f1a917b..081ecc3 100644 --- a/src/screens/auth/LoginScreen.tsx +++ b/src/screens/auth/LoginScreen.tsx @@ -12,6 +12,7 @@ import { import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { RootStackParamList } from '../../navigation/AppNavigator'; import { login, resetPassword } from '../../services/authService'; +import { getErrorMessage } from '../../utils/errorUtils'; type Props = { navigation: NativeStackNavigationProp }; @@ -29,8 +30,8 @@ export default function LoginScreen({ navigation }: Props) { setLoading(true); try { await login(email.trim(), password); - } catch (e: any) { - Alert.alert('Login failed', e.message); + } catch (error: unknown) { + Alert.alert('Login failed', getErrorMessage(error, 'Login failed. Please try again.')); } finally { setLoading(false); } @@ -45,8 +46,8 @@ export default function LoginScreen({ navigation }: Props) { try { await resetPassword(emailToReset); Alert.alert('Reset link sent', `If an account exists for ${emailToReset}, a reset email has been sent.`); - } catch (e: any) { - Alert.alert('Reset failed', e.message); + } catch (error: unknown) { + Alert.alert('Reset failed', getErrorMessage(error, 'Reset failed. Please try again.')); } } diff --git a/src/screens/auth/RegisterScreen.tsx b/src/screens/auth/RegisterScreen.tsx index 1d0ccf9..7886c03 100644 --- a/src/screens/auth/RegisterScreen.tsx +++ b/src/screens/auth/RegisterScreen.tsx @@ -12,6 +12,7 @@ import { import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { RootStackParamList } from '../../navigation/AppNavigator'; import { register } from '../../services/authService'; +import { getErrorMessage } from '../../utils/errorUtils'; type Props = { navigation: NativeStackNavigationProp }; @@ -33,8 +34,8 @@ export default function RegisterScreen({ navigation }: Props) { setLoading(true); try { await register(email.trim(), password); - } catch (e: any) { - Alert.alert('Registration failed', e.message); + } catch (error: unknown) { + Alert.alert('Registration failed', getErrorMessage(error, 'Registration failed. Please try again.')); } finally { setLoading(false); } diff --git a/src/screens/goals/CreateGoalScreen.tsx b/src/screens/goals/CreateGoalScreen.tsx index bc5b51c..0f72d50 100644 --- a/src/screens/goals/CreateGoalScreen.tsx +++ b/src/screens/goals/CreateGoalScreen.tsx @@ -22,6 +22,7 @@ import SidebarNav from '../../components/SidebarNav'; import { formatCurrency, parseNumberInput } from '../../utils/format'; import { captureError, trackEvent } from '../../services/telemetryService'; import { showToast } from '../../services/toastService'; +import { getErrorMessage } from '../../utils/errorUtils'; type Nav = NativeStackNavigationProp; @@ -113,9 +114,9 @@ export default function CreateGoalScreen() { didCompleteRef.current = true; showToast('Goal created successfully.', 'success'); navigation.replace('GoalDetail', { goalId }); - } catch (e: any) { - captureError('create_goal_submit', e); - showToast(e?.message ?? 'Failed to create goal.', 'error'); + } catch (error: unknown) { + captureError('create_goal_submit', error); + showToast(getErrorMessage(error, 'Failed to create goal.'), 'error'); } finally { setLoading(false); } diff --git a/src/screens/goals/DepositScreen.tsx b/src/screens/goals/DepositScreen.tsx index 0becb61..d426ef0 100644 --- a/src/screens/goals/DepositScreen.tsx +++ b/src/screens/goals/DepositScreen.tsx @@ -26,6 +26,7 @@ import { isE2EMode } from '../../config/runtime'; import { formatCurrency, parseNumberInput } from '../../utils/format'; import { captureError, trackEvent } from '../../services/telemetryService'; import { showToast } from '../../services/toastService'; +import { getErrorMessage } from '../../utils/errorUtils'; type Nav = NativeStackNavigationProp; type Route = RouteProp; @@ -153,9 +154,9 @@ export default function DepositScreen() { showToast('Deposit recorded.', 'success'); navigation.goBack(); } - } catch (e: any) { - captureError('deposit_submit', e); - showToast(e?.message ?? 'Failed to record deposit.', 'error'); + } catch (error: unknown) { + captureError('deposit_submit', error); + showToast(getErrorMessage(error, 'Failed to record deposit.'), 'error'); } finally { setLoading(false); } diff --git a/src/screens/goals/EditGoalScreen.tsx b/src/screens/goals/EditGoalScreen.tsx index c1920a6..2604cee 100644 --- a/src/screens/goals/EditGoalScreen.tsx +++ b/src/screens/goals/EditGoalScreen.tsx @@ -25,6 +25,7 @@ import SidebarNav from '../../components/SidebarNav'; import { formatCurrency } from '../../utils/format'; import { captureError, trackEvent } from '../../services/telemetryService'; import { showToast } from '../../services/toastService'; +import { getErrorMessage } from '../../utils/errorUtils'; type Props = NativeStackScreenProps; @@ -124,9 +125,9 @@ export default function EditGoalScreen({ navigation, route }: Props) { setWasSaved(true); showToast('Goal changes saved.', 'success'); navigation.goBack(); - } catch (e: any) { - captureError('edit_goal_save', e); - showToast(e?.message ?? 'Failed to update goal.', 'error'); + } catch (error: unknown) { + captureError('edit_goal_save', error); + showToast(getErrorMessage(error, 'Failed to update goal.'), 'error'); } finally { setLoading(false); } @@ -150,9 +151,9 @@ export default function EditGoalScreen({ navigation, route }: Props) { setWasSaved(true); showToast('Goal deleted.', 'success'); navigation.navigate('AppTabs'); - } catch (e: any) { - captureError('edit_goal_delete', e); - showToast(e?.message ?? 'Failed to delete goal.', 'error'); + } catch (error: unknown) { + captureError('edit_goal_delete', error); + showToast(getErrorMessage(error, 'Failed to delete goal.'), 'error'); } finally { setLoading(false); } diff --git a/src/services/goalService.ts b/src/services/goalService.ts index df7c19d..729bf59 100644 --- a/src/services/goalService.ts +++ b/src/services/goalService.ts @@ -39,8 +39,22 @@ export interface Goal { currentBalance: number; } +/** Typed shape of a goal document as stored in Firestore. */ +interface GoalDocData { + userId: string; + name: string; + targetAmount: number; + monthlyContribution: number; + annualInterestRate: number; + timelineMonths?: number; + visualTheme?: ThemeType; + createdAt: Timestamp; + completedAt?: Timestamp; + currentBalance?: number; +} + function mapGoal(snap: QueryDocumentSnapshot): Goal { - const d = snap.data(); + const d = snap.data() as GoalDocData; return { id: snap.id, userId: d.userId, @@ -82,7 +96,7 @@ export async function updateGoalBalance(goalId: string, newBalance: number): Pro export async function markGoalCompleted(goalId: string): Promise { if (isE2EMode) { - e2eUpdateGoal(goalId, { completedAt: serverTimestamp() as unknown as Timestamp }); + e2eUpdateGoal(goalId, { completedAt: Timestamp.now() }); return; } await updateDoc(doc(db, 'goals', goalId), { completedAt: serverTimestamp() }); diff --git a/src/utils/errorUtils.ts b/src/utils/errorUtils.ts new file mode 100644 index 0000000..bd8c6c6 --- /dev/null +++ b/src/utils/errorUtils.ts @@ -0,0 +1,7 @@ +/** + * Safely extracts a human-readable message from an unknown caught value. + * Use this in catch blocks instead of `(e: any).message`. + */ +export function getErrorMessage(error: unknown, fallback = 'An unexpected error occurred.'): string { + return error instanceof Error ? error.message : fallback; +} diff --git a/tests/errorUtils.test.ts b/tests/errorUtils.test.ts new file mode 100644 index 0000000..f74b212 --- /dev/null +++ b/tests/errorUtils.test.ts @@ -0,0 +1,30 @@ +import { describe, it } from 'node:test'; +import assert from 'node:assert/strict'; +import { getErrorMessage } from '../src/utils/errorUtils'; + +describe('getErrorMessage', () => { + it('returns the error message when given an Error instance', () => { + const err = new Error('something went wrong'); + assert.equal(getErrorMessage(err), 'something went wrong'); + }); + + it('returns the fallback when given a non-Error value (string)', () => { + assert.equal(getErrorMessage('raw string'), 'An unexpected error occurred.'); + }); + + it('returns the fallback when given a non-Error value (object)', () => { + assert.equal(getErrorMessage({ code: 42 }), 'An unexpected error occurred.'); + }); + + it('returns the fallback when given null', () => { + assert.equal(getErrorMessage(null), 'An unexpected error occurred.'); + }); + + it('returns the fallback when given undefined', () => { + assert.equal(getErrorMessage(undefined), 'An unexpected error occurred.'); + }); + + it('uses the custom fallback when provided', () => { + assert.equal(getErrorMessage('not an Error', 'custom fallback'), 'custom fallback'); + }); +});