From 51bca2598e043e07a12674edd6220de0640f6d7c Mon Sep 17 00:00:00 2001 From: rafaeelricco Date: Tue, 21 Apr 2026 10:06:19 -0300 Subject: [PATCH 01/13] Remove `packageManager` metadata from package.json --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 0b0a4fc..6298b0f 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,5 @@ "openai": "^6.25.0", "picocolors": "^1.1.1", "react": "^19.2.4" - }, - "packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be" + } } From 29ad547368a2f6ddfaa1e34c2469e0068b6efdc2 Mon Sep 17 00:00:00 2001 From: rafaeelricco Date: Tue, 21 Apr 2026 10:26:49 -0300 Subject: [PATCH 02/13] Add ESLint configuration and lint scripts - Add a new `eslint.config.js` for TypeScript source files with SonarJS and a cognitive complexity limit. - Add `lint` and `lint:ci` scripts to run ESLint across the project. - Add `eslint`, `typescript-eslint`, and `eslint-plugin-sonarjs` as development dependencies and update the lockfile. --- eslint.config.js | 14 + package.json | 9 +- pnpm-lock.yaml | 1047 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1068 insertions(+), 2 deletions(-) create mode 100644 eslint.config.js diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..538ca32 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,14 @@ +import tseslint from "typescript-eslint"; +import sonarjs from "eslint-plugin-sonarjs"; + +export default tseslint.config( + { ignores: ["dist/**", "node_modules/**", "**/*.d.ts"] }, + { + files: ["index.ts", "src/**/*.{ts,tsx}"], + languageOptions: { parser: tseslint.parser }, + plugins: { sonarjs }, + rules: { + "sonarjs/cognitive-complexity": ["error", 15] + } + } +); diff --git a/package.json b/package.json index 6298b0f..7ed488e 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,9 @@ "build": "tsup", "install:global": "npm link", "format": "npx prettier . --write", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "lint": "eslint .", + "lint:ci": "eslint ." }, "repository": { "type": "git", @@ -30,11 +32,14 @@ "@types/luxon": "^3.7.1", "@types/node": "^22", "@types/react": "^19.2.14", + "eslint": "^9.39.4", + "eslint-plugin-sonarjs": "^4.0.3", "prettier": "^3.8.1", "prettier-plugin-tailwindcss": "^0.7.2", "react-devtools-core": "^7.0.1", "tsup": "^8", - "tsx": "^4" + "tsx": "^4", + "typescript-eslint": "^8.59.0" }, "peerDependencies": { "typescript": "^5" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 51c2d21..925b90d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,6 +65,12 @@ importers: "@types/react": specifier: ^19.2.14 version: 19.2.14 + eslint: + specifier: ^9.39.4 + version: 9.39.4 + eslint-plugin-sonarjs: + specifier: ^4.0.3 + version: 4.0.3(eslint@9.39.4) prettier: specifier: ^3.8.1 version: 3.8.1 @@ -80,6 +86,9 @@ importers: tsx: specifier: ^4 version: 4.21.0 + typescript-eslint: + specifier: ^8.59.0 + version: 8.59.0(eslint@9.39.4)(typescript@5.9.3) packages: "@alcalzone/ansi-tokenize@0.2.5": @@ -297,11 +306,83 @@ packages: cpu: [x64] os: [win32] + "@eslint-community/eslint-utils@4.9.1": + resolution: + { integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + "@eslint-community/regexpp@4.12.2": + resolution: + { integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== } + engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + + "@eslint/config-array@0.21.2": + resolution: + { integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/config-helpers@0.4.2": + resolution: + { integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/core@0.17.0": + resolution: + { integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/eslintrc@3.3.5": + resolution: + { integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/js@9.39.4": + resolution: + { integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/object-schema@2.1.7": + resolution: + { integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/plugin-kit@0.4.1": + resolution: + { integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + "@google/generative-ai@0.24.1": resolution: { integrity: sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q== } engines: { node: ">=18.0.0" } + "@humanfs/core@0.19.2": + resolution: + { integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA== } + engines: { node: ">=18.18.0" } + + "@humanfs/node@0.16.8": + resolution: + { integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ== } + engines: { node: ">=18.18.0" } + + "@humanfs/types@0.15.0": + resolution: + { integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q== } + engines: { node: ">=18.18.0" } + + "@humanwhocodes/module-importer@1.0.1": + resolution: + { integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== } + engines: { node: ">=12.22" } + + "@humanwhocodes/retry@0.4.3": + resolution: + { integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== } + engines: { node: ">=18.18" } + "@jridgewell/gen-mapping@0.3.13": resolution: { integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== } @@ -514,6 +595,10 @@ packages: resolution: { integrity: sha512-yEuhWTRMXJkIWiaM58c5kuRot5HFccv9kjjgy0fBZG0+tYb+sMBaxHNPVqyZXspGGlmwDOQ1d3BsygWpWlW17w== } + "@types/json-schema@7.0.15": + resolution: + { integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== } + "@types/luxon@3.7.1": resolution: { integrity: sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg== } @@ -546,6 +631,81 @@ packages: resolution: { integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ== } + "@typescript-eslint/eslint-plugin@8.59.0": + resolution: + { integrity: sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + "@typescript-eslint/parser": ^8.59.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + + "@typescript-eslint/parser@8.59.0": + resolution: + { integrity: sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + + "@typescript-eslint/project-service@8.59.0": + resolution: + { integrity: sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + + "@typescript-eslint/scope-manager@8.59.0": + resolution: + { integrity: sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@typescript-eslint/tsconfig-utils@8.59.0": + resolution: + { integrity: sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + + "@typescript-eslint/type-utils@8.59.0": + resolution: + { integrity: sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + + "@typescript-eslint/types@8.59.0": + resolution: + { integrity: sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@typescript-eslint/typescript-estree@8.59.0": + resolution: + { integrity: sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + + "@typescript-eslint/utils@8.59.0": + resolution: + { integrity: sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + + "@typescript-eslint/visitor-keys@8.59.0": + resolution: + { integrity: sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + acorn-jsx@5.3.2: + resolution: + { integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== } + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn@8.16.0: resolution: { integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== } @@ -557,6 +717,10 @@ packages: { integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== } engines: { node: ">= 14" } + ajv@6.14.0: + resolution: + { integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw== } + ansi-escapes@7.3.0: resolution: { integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg== } @@ -572,6 +736,11 @@ packages: { integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== } engines: { node: ">=12" } + ansi-styles@4.3.0: + resolution: + { integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== } + engines: { node: ">=8" } + ansi-styles@6.2.3: resolution: { integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== } @@ -581,11 +750,24 @@ packages: resolution: { integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== } + argparse@2.0.1: + resolution: + { integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== } + auto-bind@5.0.1: resolution: { integrity: sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg== } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + balanced-match@1.0.2: + resolution: + { integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== } + + balanced-match@4.0.4: + resolution: + { integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== } + engines: { node: 18 || 20 || >=22 } + base64-js@1.5.1: resolution: { integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== } @@ -594,10 +776,24 @@ packages: resolution: { integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ== } + brace-expansion@1.1.14: + resolution: + { integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g== } + + brace-expansion@5.0.5: + resolution: + { integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ== } + engines: { node: 18 || 20 || >=22 } + buffer-equal-constant-time@1.0.1: resolution: { integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== } + builtin-modules@3.3.0: + resolution: + { integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== } + engines: { node: ">=6" } + bundle-name@4.1.0: resolution: { integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== } @@ -610,11 +806,26 @@ packages: peerDependencies: esbuild: ">=0.18" + bytes@3.1.2: + resolution: + { integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== } + engines: { node: ">= 0.8" } + cac@6.7.14: resolution: { integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== } engines: { node: ">=8" } + callsites@3.1.0: + resolution: + { integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== } + engines: { node: ">=6" } + + chalk@4.1.2: + resolution: + { integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== } + engines: { node: ">=10" } + chalk@5.6.2: resolution: { integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== } @@ -650,11 +861,24 @@ packages: { integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA== } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + color-convert@2.0.1: + resolution: + { integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== } + engines: { node: ">=7.0.0" } + + color-name@1.1.4: + resolution: + { integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== } + commander@4.1.1: resolution: { integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== } engines: { node: ">= 6" } + concat-map@0.0.1: + resolution: + { integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== } + confbox@0.1.8: resolution: { integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w== } @@ -669,6 +893,11 @@ packages: { integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ== } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + cross-spawn@7.0.6: + resolution: + { integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== } + engines: { node: ">= 8" } + csstype@3.2.3: resolution: { integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== } @@ -688,6 +917,10 @@ packages: supports-color: optional: true + deep-is@0.1.4: + resolution: + { integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== } + default-browser-id@5.0.1: resolution: { integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q== } @@ -735,10 +968,89 @@ packages: { integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== } engines: { node: ">=8" } + escape-string-regexp@4.0.0: + resolution: + { integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== } + engines: { node: ">=10" } + + eslint-plugin-sonarjs@4.0.3: + resolution: + { integrity: sha512-5drkJKLC9qQddIiaATV0e8+ygbUc7b0Ti6VB7M2d3jmKNh3X0RaiIJYTs3dr9xnlhlrxo+/s1FoO3Jgv6O/c7g== } + peerDependencies: + eslint: ^8.0.0 || ^9.0.0 || ^10.0.0 + + eslint-scope@8.4.0: + resolution: + { integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint-visitor-keys@3.4.3: + resolution: + { integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + eslint-visitor-keys@4.2.1: + resolution: + { integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint-visitor-keys@5.0.1: + resolution: + { integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + eslint@9.39.4: + resolution: + { integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + hasBin: true + peerDependencies: + jiti: "*" + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: + { integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + esquery@1.7.0: + resolution: + { integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== } + engines: { node: ">=0.10" } + + esrecurse@4.3.0: + resolution: + { integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== } + engines: { node: ">=4.0" } + + estraverse@5.3.0: + resolution: + { integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== } + engines: { node: ">=4.0" } + + esutils@2.0.3: + resolution: + { integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== } + engines: { node: ">=0.10.0" } + extend@3.0.2: resolution: { integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== } + fast-deep-equal@3.1.3: + resolution: + { integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== } + + fast-json-stable-stringify@2.1.0: + resolution: + { integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== } + + fast-levenshtein@2.0.6: + resolution: + { integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== } + fdir@6.5.0: resolution: { integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== } @@ -754,10 +1066,29 @@ packages: { integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== } engines: { node: ^12.20 || >= 14.13 } + file-entry-cache@8.0.0: + resolution: + { integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== } + engines: { node: ">=16.0.0" } + + find-up@5.0.0: + resolution: + { integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== } + engines: { node: ">=10" } + fix-dts-default-cjs-exports@1.0.1: resolution: { integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg== } + flat-cache@4.0.1: + resolution: + { integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== } + engines: { node: ">=16" } + + flatted@3.4.2: + resolution: + { integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA== } + fluture@14.0.0: resolution: { integrity: sha512-pENtLF948a8DfduVKugT8edTAbFi4rBS94xjHwzLanQqIu5PYtLGl+xqs6H8TaIRL7z/B0cDpswdINzH/HRUGA== } @@ -774,6 +1105,10 @@ packages: engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } os: [darwin] + functional-red-black-tree@1.0.1: + resolution: + { integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== } + gaxios@7.1.4: resolution: { integrity: sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA== } @@ -793,6 +1128,21 @@ packages: resolution: { integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw== } + glob-parent@6.0.2: + resolution: + { integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== } + engines: { node: ">=10.13.0" } + + globals@14.0.0: + resolution: + { integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== } + engines: { node: ">=18" } + + globals@17.5.0: + resolution: + { integrity: sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g== } + engines: { node: ">=18" } + google-auth-library@10.6.2: resolution: { integrity: sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw== } @@ -803,11 +1153,36 @@ packages: { integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA== } engines: { node: ">=14" } + has-flag@4.0.0: + resolution: + { integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== } + engines: { node: ">=8" } + https-proxy-agent@7.0.6: resolution: { integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== } engines: { node: ">= 14" } + ignore@5.3.2: + resolution: + { integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== } + engines: { node: ">= 4" } + + ignore@7.0.5: + resolution: + { integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== } + engines: { node: ">= 4" } + + import-fresh@3.3.1: + resolution: + { integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== } + engines: { node: ">=6" } + + imurmurhash@0.1.4: + resolution: + { integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== } + engines: { node: ">=0.8.19" } + indent-string@5.0.0: resolution: { integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg== } @@ -833,6 +1208,11 @@ packages: engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } hasBin: true + is-extglob@2.1.1: + resolution: + { integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== } + engines: { node: ">=0.10.0" } + is-fullwidth-code-point@3.0.0: resolution: { integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== } @@ -843,6 +1223,11 @@ packages: { integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ== } engines: { node: ">=18" } + is-glob@4.0.3: + resolution: + { integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== } + engines: { node: ">=0.10.0" } + is-in-ci@2.0.0: resolution: { integrity: sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w== } @@ -865,20 +1250,46 @@ packages: { integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw== } engines: { node: ">=16" } + isexe@2.0.0: + resolution: + { integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== } + joycon@3.1.1: resolution: { integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== } engines: { node: ">=10" } + js-yaml@4.1.1: + resolution: + { integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== } + hasBin: true + json-bigint@1.0.0: resolution: { integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== } + json-buffer@3.0.1: + resolution: + { integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== } + json-schema-to-ts@3.1.1: resolution: { integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g== } engines: { node: ">=16" } + json-schema-traverse@0.4.1: + resolution: + { integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== } + + json-stable-stringify-without-jsonify@1.0.1: + resolution: + { integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== } + + jsx-ast-utils-x@0.1.0: + resolution: + { integrity: sha512-eQQBjBnsVtGacsG9uJNB8qOr3yA8rga4wAaGG1qRcBzSIvfhERLrWxMAM1hp5fcS6Abo8M4+bUBTekYR0qTPQw== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + jwa@2.0.1: resolution: { integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg== } @@ -887,6 +1298,15 @@ packages: resolution: { integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA== } + keyv@4.5.4: + resolution: + { integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== } + + levn@0.4.1: + resolution: + { integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== } + engines: { node: ">= 0.8.0" } + lilconfig@3.1.3: resolution: { integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== } @@ -901,6 +1321,15 @@ packages: { integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg== } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + locate-path@6.0.0: + resolution: + { integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== } + engines: { node: ">=10" } + + lodash.merge@4.6.2: + resolution: + { integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== } + luxon@3.7.2: resolution: { integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew== } @@ -915,6 +1344,15 @@ packages: { integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== } engines: { node: ">=6" } + minimatch@10.2.5: + resolution: + { integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg== } + engines: { node: 18 || 20 || >=22 } + + minimatch@3.1.5: + resolution: + { integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== } + mlly@1.8.1: resolution: { integrity: sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ== } @@ -927,6 +1365,10 @@ packages: resolution: { integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== } + natural-compare@1.4.0: + resolution: + { integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== } + node-domexception@1.0.0: resolution: { integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== } @@ -966,11 +1408,41 @@ packages: zod: optional: true + optionator@0.9.4: + resolution: + { integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== } + engines: { node: ">= 0.8.0" } + + p-limit@3.1.0: + resolution: + { integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== } + engines: { node: ">=10" } + + p-locate@5.0.0: + resolution: + { integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== } + engines: { node: ">=10" } + + parent-module@1.0.1: + resolution: + { integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== } + engines: { node: ">=6" } + patch-console@2.0.0: resolution: { integrity: sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA== } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + path-exists@4.0.0: + resolution: + { integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== } + engines: { node: ">=8" } + + path-key@3.1.1: + resolution: + { integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== } + engines: { node: ">=8" } + pathe@2.0.3: resolution: { integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== } @@ -1017,6 +1489,11 @@ packages: { integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A== } engines: { node: ">=20" } + prelude-ls@1.2.1: + resolution: + { integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== } + engines: { node: ">= 0.8.0" } + prettier-plugin-tailwindcss@0.7.2: resolution: { integrity: sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA== } @@ -1079,6 +1556,11 @@ packages: engines: { node: ">=14" } hasBin: true + punycode@2.3.1: + resolution: + { integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== } + engines: { node: ">=6" } + react-devtools-core@7.0.1: resolution: { integrity: sha512-C3yNvRHaizlpiASzy7b9vbnBGLrhvdhl1CbdU6EnZgxPNbai60szdLtl+VL76UNOt5bOoVTOz5rNWZxgGt+Gsw== } @@ -1100,6 +1582,21 @@ packages: { integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== } engines: { node: ">= 14.18.0" } + refa@0.12.1: + resolution: + { integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g== } + engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + + regexp-ast-analysis@0.7.1: + resolution: + { integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A== } + engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + + resolve-from@4.0.0: + resolution: + { integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== } + engines: { node: ">=4" } + resolve-from@5.0.0: resolution: { integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== } @@ -1141,6 +1638,27 @@ packages: resolution: { integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== } + scslre@0.3.0: + resolution: + { integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ== } + engines: { node: ^14.0.0 || >=16.0.0 } + + semver@7.7.4: + resolution: + { integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== } + engines: { node: ">=10" } + hasBin: true + + shebang-command@2.0.0: + resolution: + { integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== } + engines: { node: ">=8" } + + shebang-regex@3.0.0: + resolution: + { integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== } + engines: { node: ">=8" } + shell-quote@1.8.3: resolution: { integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== } @@ -1194,12 +1712,22 @@ packages: { integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== } engines: { node: ">=12" } + strip-json-comments@3.1.1: + resolution: + { integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== } + engines: { node: ">=8" } + sucrase@3.35.1: resolution: { integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw== } engines: { node: ">=16 || 14 >=14.17" } hasBin: true + supports-color@7.2.0: + resolution: + { integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== } + engines: { node: ">=8" } + tagged-tag@1.0.0: resolution: { integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng== } @@ -1237,6 +1765,13 @@ packages: resolution: { integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw== } + ts-api-utils@2.5.0: + resolution: + { integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA== } + engines: { node: ">=18.12" } + peerDependencies: + typescript: ">=4.8.4" + ts-interface-checker@0.1.13: resolution: { integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== } @@ -1267,11 +1802,24 @@ packages: engines: { node: ">=18.0.0" } hasBin: true + type-check@0.4.0: + resolution: + { integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== } + engines: { node: ">= 0.8.0" } + type-fest@5.4.4: resolution: { integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw== } engines: { node: ">=20" } + typescript-eslint@8.59.0: + resolution: + { integrity: sha512-BU3ONW9X+v90EcCH9ZS6LMackcVtxRLlI3XrYyqZIwVSHIk7Qf7bFw1z0M9Q0IUxhTMZCf8piY9hTYaNEIASrw== } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + typescript@5.9.3: resolution: { integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== } @@ -1286,16 +1834,31 @@ packages: resolution: { integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== } + uri-js@4.4.1: + resolution: + { integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== } + web-streams-polyfill@3.3.3: resolution: { integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== } engines: { node: ">= 8" } + which@2.0.2: + resolution: + { integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== } + engines: { node: ">= 8" } + hasBin: true + widest-line@6.0.0: resolution: { integrity: sha512-U89AsyEeAsyoF0zVJBkG9zBgekjgjK7yk9sje3F4IQpXBJ10TF6ByLlIfjMhcmHMJgHZI4KHt4rdNfktzxIAMA== } engines: { node: ">=20" } + word-wrap@1.2.5: + resolution: + { integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== } + engines: { node: ">=0.10.0" } + wrap-ansi@9.0.2: resolution: { integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww== } @@ -1332,6 +1895,11 @@ packages: { integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg== } engines: { node: ">=20" } + yocto-queue@0.1.0: + resolution: + { integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== } + engines: { node: ">=10" } + yoga-layout@3.2.1: resolution: { integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ== } @@ -1438,8 +2006,70 @@ snapshots: "@esbuild/win32-x64@0.27.4": optional: true + "@eslint-community/eslint-utils@4.9.1(eslint@9.39.4)": + dependencies: + eslint: 9.39.4 + eslint-visitor-keys: 3.4.3 + + "@eslint-community/regexpp@4.12.2": {} + + "@eslint/config-array@0.21.2": + dependencies: + "@eslint/object-schema": 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + "@eslint/config-helpers@0.4.2": + dependencies: + "@eslint/core": 0.17.0 + + "@eslint/core@0.17.0": + dependencies: + "@types/json-schema": 7.0.15 + + "@eslint/eslintrc@3.3.5": + dependencies: + ajv: 6.14.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + "@eslint/js@9.39.4": {} + + "@eslint/object-schema@2.1.7": {} + + "@eslint/plugin-kit@0.4.1": + dependencies: + "@eslint/core": 0.17.0 + levn: 0.4.1 + "@google/generative-ai@0.24.1": {} + "@humanfs/core@0.19.2": + dependencies: + "@humanfs/types": 0.15.0 + + "@humanfs/node@0.16.8": + dependencies: + "@humanfs/core": 0.19.2 + "@humanfs/types": 0.15.0 + "@humanwhocodes/retry": 0.4.3 + + "@humanfs/types@0.15.0": {} + + "@humanwhocodes/module-importer@1.0.1": {} + + "@humanwhocodes/retry@0.4.3": {} + "@jridgewell/gen-mapping@0.3.13": dependencies: "@jridgewell/sourcemap-codec": 1.5.5 @@ -1564,6 +2194,8 @@ snapshots: "@types/node": 22.19.15 "@types/prop-types": 15.7.15 + "@types/json-schema@7.0.15": {} + "@types/luxon@3.7.1": {} "@types/node@22.19.15": @@ -1589,10 +2221,112 @@ snapshots: "@types/http-errors": 2.0.5 "@types/node": 22.19.15 + "@typescript-eslint/eslint-plugin@8.59.0(@typescript-eslint/parser@8.59.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)": + dependencies: + "@eslint-community/regexpp": 4.12.2 + "@typescript-eslint/parser": 8.59.0(eslint@9.39.4)(typescript@5.9.3) + "@typescript-eslint/scope-manager": 8.59.0 + "@typescript-eslint/type-utils": 8.59.0(eslint@9.39.4)(typescript@5.9.3) + "@typescript-eslint/utils": 8.59.0(eslint@9.39.4)(typescript@5.9.3) + "@typescript-eslint/visitor-keys": 8.59.0 + eslint: 9.39.4 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/parser@8.59.0(eslint@9.39.4)(typescript@5.9.3)": + dependencies: + "@typescript-eslint/scope-manager": 8.59.0 + "@typescript-eslint/types": 8.59.0 + "@typescript-eslint/typescript-estree": 8.59.0(typescript@5.9.3) + "@typescript-eslint/visitor-keys": 8.59.0 + debug: 4.4.3 + eslint: 9.39.4 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/project-service@8.59.0(typescript@5.9.3)": + dependencies: + "@typescript-eslint/tsconfig-utils": 8.59.0(typescript@5.9.3) + "@typescript-eslint/types": 8.59.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/scope-manager@8.59.0": + dependencies: + "@typescript-eslint/types": 8.59.0 + "@typescript-eslint/visitor-keys": 8.59.0 + + "@typescript-eslint/tsconfig-utils@8.59.0(typescript@5.9.3)": + dependencies: + typescript: 5.9.3 + + "@typescript-eslint/type-utils@8.59.0(eslint@9.39.4)(typescript@5.9.3)": + dependencies: + "@typescript-eslint/types": 8.59.0 + "@typescript-eslint/typescript-estree": 8.59.0(typescript@5.9.3) + "@typescript-eslint/utils": 8.59.0(eslint@9.39.4)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.4 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/types@8.59.0": {} + + "@typescript-eslint/typescript-estree@8.59.0(typescript@5.9.3)": + dependencies: + "@typescript-eslint/project-service": 8.59.0(typescript@5.9.3) + "@typescript-eslint/tsconfig-utils": 8.59.0(typescript@5.9.3) + "@typescript-eslint/types": 8.59.0 + "@typescript-eslint/visitor-keys": 8.59.0 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/utils@8.59.0(eslint@9.39.4)(typescript@5.9.3)": + dependencies: + "@eslint-community/eslint-utils": 4.9.1(eslint@9.39.4) + "@typescript-eslint/scope-manager": 8.59.0 + "@typescript-eslint/types": 8.59.0 + "@typescript-eslint/typescript-estree": 8.59.0(typescript@5.9.3) + eslint: 9.39.4 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/visitor-keys@8.59.0": + dependencies: + "@typescript-eslint/types": 8.59.0 + eslint-visitor-keys: 5.0.1 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn@8.16.0: {} agent-base@7.1.4: {} + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + ansi-escapes@7.3.0: dependencies: environment: 1.1.0 @@ -1601,18 +2335,39 @@ snapshots: ansi-regex@6.2.2: {} + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + ansi-styles@6.2.3: {} any-promise@1.3.0: {} + argparse@2.0.1: {} + auto-bind@5.0.1: {} + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + base64-js@1.5.1: {} bignumber.js@9.3.1: {} + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.5: + dependencies: + balanced-match: 4.0.4 + buffer-equal-constant-time@1.0.1: {} + builtin-modules@3.3.0: {} + bundle-name@4.1.0: dependencies: run-applescript: 7.1.0 @@ -1622,8 +2377,17 @@ snapshots: esbuild: 0.27.4 load-tsconfig: 0.2.5 + bytes@3.1.2: {} + cac@6.7.14: {} + callsites@3.1.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + chalk@5.6.2: {} chokidar@4.0.3: @@ -1651,14 +2415,28 @@ snapshots: dependencies: convert-to-spaces: 2.0.1 + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + commander@4.1.1: {} + concat-map@0.0.1: {} + confbox@0.1.8: {} consola@3.4.2: {} convert-to-spaces@2.0.1: {} + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + csstype@3.2.3: {} data-uri-to-buffer@4.0.1: {} @@ -1667,6 +2445,8 @@ snapshots: dependencies: ms: 2.1.3 + deep-is@0.1.4: {} + default-browser-id@5.0.1: {} default-browser@5.5.0: @@ -1719,8 +2499,100 @@ snapshots: escape-string-regexp@2.0.0: {} + escape-string-regexp@4.0.0: {} + + eslint-plugin-sonarjs@4.0.3(eslint@9.39.4): + dependencies: + "@eslint-community/regexpp": 4.12.2 + builtin-modules: 3.3.0 + bytes: 3.1.2 + eslint: 9.39.4 + functional-red-black-tree: 1.0.1 + globals: 17.5.0 + jsx-ast-utils-x: 0.1.0 + lodash.merge: 4.6.2 + minimatch: 10.2.5 + scslre: 0.3.0 + semver: 7.7.4 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.4: + dependencies: + "@eslint-community/eslint-utils": 4.9.1(eslint@9.39.4) + "@eslint-community/regexpp": 4.12.2 + "@eslint/config-array": 0.21.2 + "@eslint/config-helpers": 0.4.2 + "@eslint/core": 0.17.0 + "@eslint/eslintrc": 3.3.5 + "@eslint/js": 9.39.4 + "@eslint/plugin-kit": 0.4.1 + "@humanfs/node": 0.16.8 + "@humanwhocodes/module-importer": 1.0.1 + "@humanwhocodes/retry": 0.4.3 + "@types/estree": 1.0.8 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + extend@3.0.2: {} + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -1730,12 +2602,28 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + fix-dts-default-cjs-exports@1.0.1: dependencies: magic-string: 0.30.21 mlly: 1.8.1 rollup: 4.59.0 + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + fluture@14.0.0: dependencies: sanctuary-show: 2.0.0 @@ -1748,6 +2636,8 @@ snapshots: fsevents@2.3.3: optional: true + functional-red-black-tree@1.0.1: {} + gaxios@7.1.4: dependencies: extend: 3.0.2 @@ -1770,6 +2660,14 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@17.5.0: {} + google-auth-library@10.6.2: dependencies: base64-js: 1.5.1 @@ -1783,6 +2681,8 @@ snapshots: google-logging-utils@1.1.3: {} + has-flag@4.0.0: {} + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -1790,6 +2690,17 @@ snapshots: transitivePeerDependencies: - supports-color + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + indent-string@5.0.0: {} ink@6.8.0(@types/react@19.2.14)(react-devtools-core@7.0.1)(react@19.2.4): @@ -1829,12 +2740,18 @@ snapshots: is-docker@3.0.0: {} + is-extglob@2.1.1: {} + is-fullwidth-code-point@3.0.0: {} is-fullwidth-code-point@5.1.0: dependencies: get-east-asian-width: 1.5.0 + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + is-in-ci@2.0.0: {} is-in-ssh@1.0.0: {} @@ -1847,17 +2764,31 @@ snapshots: dependencies: is-inside-container: 1.0.0 + isexe@2.0.0: {} + joycon@3.1.1: {} + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + json-bigint@1.0.0: dependencies: bignumber.js: 9.3.1 + json-buffer@3.0.1: {} + json-schema-to-ts@3.1.1: dependencies: "@babel/runtime": 7.29.2 ts-algebra: 2.0.0 + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + jsx-ast-utils-x@0.1.0: {} + jwa@2.0.1: dependencies: buffer-equal-constant-time: 1.0.1 @@ -1869,12 +2800,27 @@ snapshots: jwa: 2.0.1 safe-buffer: 5.2.1 + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} load-tsconfig@0.2.5: {} + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + luxon@3.7.2: {} magic-string@0.30.21: @@ -1883,6 +2829,14 @@ snapshots: mimic-fn@2.1.0: {} + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.5 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.14 + mlly@1.8.1: dependencies: acorn: 8.16.0 @@ -1898,6 +2852,8 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 + natural-compare@1.4.0: {} + node-domexception@1.0.0: {} node-fetch@3.3.2: @@ -1925,8 +2881,33 @@ snapshots: optionalDependencies: ws: 8.19.0 + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + patch-console@2.0.0: {} + path-exists@4.0.0: {} + + path-key@3.1.1: {} + pathe@2.0.3: {} picocolors@1.1.1: {} @@ -1949,12 +2930,16 @@ snapshots: powershell-utils@0.1.0: {} + prelude-ls@1.2.1: {} + prettier-plugin-tailwindcss@0.7.2(prettier@3.8.1): dependencies: prettier: 3.8.1 prettier@3.8.1: {} + punycode@2.3.1: {} + react-devtools-core@7.0.1: dependencies: shell-quote: 1.8.3 @@ -1972,6 +2957,17 @@ snapshots: readdirp@4.1.2: {} + refa@0.12.1: + dependencies: + "@eslint-community/regexpp": 4.12.2 + + regexp-ast-analysis@0.7.1: + dependencies: + "@eslint-community/regexpp": 4.12.2 + refa: 0.12.1 + + resolve-from@4.0.0: {} + resolve-from@5.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -2022,6 +3018,20 @@ snapshots: scheduler@0.27.0: {} + scslre@0.3.0: + dependencies: + "@eslint-community/regexpp": 4.12.2 + refa: 0.12.1 + regexp-ast-analysis: 0.7.1 + + semver@7.7.4: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + shell-quote@1.8.3: {} signal-exit@3.0.7: {} @@ -2064,6 +3074,8 @@ snapshots: dependencies: ansi-regex: 6.2.2 + strip-json-comments@3.1.1: {} + sucrase@3.35.1: dependencies: "@jridgewell/gen-mapping": 0.3.13 @@ -2074,6 +3086,10 @@ snapshots: tinyglobby: 0.2.15 ts-interface-checker: 0.1.13 + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + tagged-tag@1.0.0: {} terminal-size@4.0.1: {} @@ -2097,6 +3113,10 @@ snapshots: ts-algebra@2.0.0: {} + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + ts-interface-checker@0.1.13: {} tsup@8.5.1(tsx@4.21.0)(typescript@5.9.3): @@ -2133,22 +3153,47 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + type-fest@5.4.4: dependencies: tagged-tag: 1.0.0 + typescript-eslint@8.59.0(eslint@9.39.4)(typescript@5.9.3): + dependencies: + "@typescript-eslint/eslint-plugin": 8.59.0(@typescript-eslint/parser@8.59.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + "@typescript-eslint/parser": 8.59.0(eslint@9.39.4)(typescript@5.9.3) + "@typescript-eslint/typescript-estree": 8.59.0(typescript@5.9.3) + "@typescript-eslint/utils": 8.59.0(eslint@9.39.4)(typescript@5.9.3) + eslint: 9.39.4 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + typescript@5.9.3: {} ufo@1.6.3: {} undici-types@6.21.0: {} + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + web-streams-polyfill@3.3.3: {} + which@2.0.2: + dependencies: + isexe: 2.0.0 + widest-line@6.0.0: dependencies: string-width: 8.2.0 + word-wrap@1.2.5: {} + wrap-ansi@9.0.2: dependencies: ansi-styles: 6.2.3 @@ -2164,4 +3209,6 @@ snapshots: is-wsl: 3.1.1 powershell-utils: 0.1.0 + yocto-queue@0.1.0: {} + yoga-layout@3.2.1: {} From 19733cc6d31bbce74ac78c144337684d1bba8dac Mon Sep 17 00:00:00 2001 From: rafaeelricco Date: Tue, 21 Apr 2026 10:55:30 -0300 Subject: [PATCH 03/13] Add cognitive complexity lint job to PR validation - Add a new GitHub Actions job in `pr-validate.yml` to run `pnpm run lint:ci` on pull requests. - Set up repository checkout, Node.js 24, pnpm installation, and dependency installation for the lint workflow. - Keep the new job scoped to read-only repository contents permissions. --- .github/workflows/pr-validate.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/pr-validate.yml b/.github/workflows/pr-validate.yml index a832517..942eef8 100644 --- a/.github/workflows/pr-validate.yml +++ b/.github/workflows/pr-validate.yml @@ -37,6 +37,29 @@ jobs: GOOGLE_CLIENT_SECRET: pr-validation-not-a-real-secret run: pnpm run build + lint: + name: Cognitive complexity + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + + - name: Install pnpm + run: npm install -g pnpm + + - name: Install dependencies + run: pnpm install + + - name: Lint (cognitive complexity) + run: pnpm run lint:ci + publish-preview: name: Publish preview runs-on: ubuntu-latest From 872f14b104b8bb5a0b4189277d221fef1d23f62b Mon Sep 17 00:00:00 2001 From: rafaeelricco Date: Tue, 21 Apr 2026 10:55:53 -0300 Subject: [PATCH 04/13] Refactor model selector input handling and clean up utility code - Split `ModelSelector` keyboard handling into focused lifecycle, navigation, edit, and cursor helpers while preserving existing input behavior. - Add the `Key` type import for Ink input handlers and keep character insertion logic in the main `useInput` callback. - Remove obsolete ESLint suppression comments from JSON and maybe utilities. - Simplify `DateOnly.compare` to use chained `Math.sign` comparisons. --- src/infra/ui/model-selector.tsx | 74 ++++++++++++++++++++------------- src/libs/json/encoder.ts | 1 - src/libs/json/schema.ts | 1 - src/libs/maybe.ts | 1 - src/libs/time.ts | 10 +---- 5 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/infra/ui/model-selector.tsx b/src/infra/ui/model-selector.tsx index 4a9012a..1b64a64 100644 --- a/src/infra/ui/model-selector.tsx +++ b/src/infra/ui/model-selector.tsx @@ -2,7 +2,7 @@ export { ModelSelector, type Model, type ModelSelectorProps }; import * as React from "react"; -import { Box, Text, useInput, useApp } from "ink"; +import { Box, Text, useInput, useApp, type Key } from "ink"; import chalk from "chalk"; @@ -64,19 +64,11 @@ const ModelSelector = ({ models, onSelect, onCancel }: ModelSelectorProps) => { return fuzzyMatch(q, m.id) || (m.description && fuzzyMatch(q, m.description)); }); - useInput((input, key) => { + const handleLifecycle = (key: Key): boolean => { if (key.escape) { onCancel(); exit(); - return; - } - if (key.upArrow) { - setSelectedIndex((prev) => Math.max(0, prev - 1)); - return; - } - if (key.downArrow) { - setSelectedIndex((prev) => Math.min(filteredModels.length - 1, prev + 1)); - return; + return true; } if (key.return) { const selectedModel = filteredModels[selectedIndex]; @@ -86,53 +78,75 @@ const ModelSelector = ({ models, onSelect, onCancel }: ModelSelectorProps) => { onCancel(); } exit(); - return; + return true; + } + return false; + }; + + const handleNavigation = (key: Key): boolean => { + if (key.upArrow) { + setSelectedIndex((prev) => Math.max(0, prev - 1)); + return true; + } + if (key.downArrow) { + setSelectedIndex((prev) => Math.min(filteredModels.length - 1, prev + 1)); + return true; } + return false; + }; + const handleEdit = (input: string, key: Key): boolean => { // Option+Delete or Ctrl+W — delete word left if ((key.meta && key.backspace) || (key.ctrl && input === "w")) { const [next, pos] = deleteWordLeft(query, cursorOffset); setQuery(next); setCursorOffset(Math.max(0, pos)); - return; + return true; } - // Ctrl+U — delete to start of line if (key.ctrl && input === "u") { setQuery(query.slice(cursorOffset)); setCursorOffset(0); - return; + return true; + } + // Regular backspace — delete one char + if (key.backspace || key.delete) { + if (cursorOffset > 0) { + setQuery(query.slice(0, cursorOffset - 1) + query.slice(cursorOffset)); + setCursorOffset((prev) => Math.max(0, prev - 1)); + } + return true; } + return false; + }; + const handleCursor = (key: Key): boolean => { // Option+Left/Right — move cursor by word if (key.meta && key.leftArrow) { setCursorOffset(Math.max(0, wordBoundaryLeft(query, cursorOffset))); - return; + return true; } if (key.meta && key.rightArrow) { setCursorOffset(Math.min(query.length, wordBoundaryRight(query, cursorOffset))); - return; + return true; } - - // Left/Right arrow - move cursor 1 char + // Left/Right arrow — move cursor 1 char if (key.leftArrow) { setCursorOffset((prev) => Math.max(0, prev - 1)); - return; + return true; } if (key.rightArrow) { setCursorOffset((prev) => Math.min(query.length, prev + 1)); - return; - } - - // Regular backspace — delete one char - if (key.backspace || key.delete) { - if (cursorOffset > 0) { - setQuery(query.slice(0, cursorOffset - 1) + query.slice(cursorOffset)); - setCursorOffset((prev) => Math.max(0, prev - 1)); - } - return; + return true; } + return false; + }; + useInput((input, key) => { + if (handleLifecycle(key)) return; + if (handleNavigation(key)) return; + if (handleEdit(input, key)) return; + if (handleCursor(key)) return; // Regular character input if (input && !key.ctrl && !key.meta) { setQuery(query.slice(0, cursorOffset) + input + query.slice(cursorOffset)); diff --git a/src/libs/json/encoder.ts b/src/libs/json/encoder.ts index d9e9c18..eb8d029 100644 --- a/src/libs/json/encoder.ts +++ b/src/libs/json/encoder.ts @@ -31,7 +31,6 @@ import { Maybe, Nothing, Just, Nullable } from "@/libs/maybe"; import { Json, JsonObject } from "@/libs/json/types"; // Infer the type from a encoder definition -// eslint-disable-next-line @typescript-eslint/no-explicit-any type Infer> = A extends Encoder ? B : never; class Encoder { diff --git a/src/libs/json/schema.ts b/src/libs/json/schema.ts index d628b7f..2064b88 100644 --- a/src/libs/json/schema.ts +++ b/src/libs/json/schema.ts @@ -49,7 +49,6 @@ import { Maybe, Nullable } from "@/libs/maybe"; import { filterMap, mapValues } from "@/libs/helpers/object"; // Infer the type from a schema definition -// eslint-disable-next-line @typescript-eslint/no-explicit-any type Infer> = A extends Schema ? B : never; class Schema { diff --git a/src/libs/maybe.ts b/src/libs/maybe.ts index 68f6283..fd92e83 100644 --- a/src/libs/maybe.ts +++ b/src/libs/maybe.ts @@ -126,6 +126,5 @@ function mapMaybe(xs: Array, f: (v: T) => Maybe): Array { return r; } -/* eslint-disable no-var */ var CallableJust = Callable(Just) as typeof Just & typeof Just.new; var CallableNothing = Callable(Nothing) as typeof Nothing & typeof Nothing.new; diff --git a/src/libs/time.ts b/src/libs/time.ts index 8356731..5246d99 100644 --- a/src/libs/time.ts +++ b/src/libs/time.ts @@ -152,15 +152,7 @@ class DateOnly { } compare(other: DateOnly): number { - return ( - this.year > other.year ? 1 - : this.year < other.year ? -1 - : this.month > other.month ? 1 - : this.month < other.month ? -1 - : this.day > other.day ? 1 - : this.day < other.day ? -1 - : 0 - ); + return Math.sign(this.year - other.year) || Math.sign(this.month - other.month) || Math.sign(this.day - other.day); } addMonths(months: number): DateOnly { From 8d27daab08c452cca9f4b5ccba585eaaad3c11fc Mon Sep 17 00:00:00 2001 From: rafaeelricco Date: Tue, 21 Apr 2026 16:47:35 -0300 Subject: [PATCH 05/13] Lower `sonarjs/cognitive-complexity` threshold from 15 to 10 --- eslint.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eslint.config.js b/eslint.config.js index 538ca32..f6817c5 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -8,7 +8,7 @@ export default tseslint.config( languageOptions: { parser: tseslint.parser }, plugins: { sonarjs }, rules: { - "sonarjs/cognitive-complexity": ["error", 15] + "sonarjs/cognitive-complexity": ["error", 10] } } ); From df698f65ca96fc815f34d4dcaa3f9c8ae7067ced Mon Sep 17 00:00:00 2001 From: rafaeelricco Date: Tue, 21 Apr 2026 17:18:14 -0300 Subject: [PATCH 06/13] Add post-push summary note with commit and branch metadata - Introduce `renderPushNote` in `src/infra/ui/push-note.ts` to display commit, branch, remote, and range info after a push. - Extend `src/infra/git/repo.ts` with `getUpstream`, `getCommitMetadata`, `getRemoteUrl`, and `PushResult`/`PushRange`/`CommitMetadata` types. - Refactor git helpers to share an `execGitChecked` utility and parse push ranges from git output. - Wire the commit CLI to gather metadata concurrently after a successful push and render the summary note. --- src/cli/commit.ts | 15 +++- src/infra/git/repo.ts | 153 ++++++++++++++++++++++++-------------- src/infra/ui/push-note.ts | 34 +++++++++ 3 files changed, 147 insertions(+), 55 deletions(-) create mode 100644 src/infra/ui/push-note.ts diff --git a/src/cli/commit.ts b/src/cli/commit.ts index 18b3e7b..ef14bd3 100644 --- a/src/cli/commit.ts +++ b/src/cli/commit.ts @@ -11,6 +11,7 @@ import { resolveProvider } from "@/domain/llm/auth-resolver"; import { generateCommitMessage, refineCommitMessage } from "@/domain/llm/router"; import { Nothing, type Maybe, Just } from "@/libs/maybe"; import { loading } from "@/infra/ui/spinner"; +import { renderPushNote } from "@/infra/ui/push-note"; import color from "picocolors"; @@ -87,7 +88,19 @@ class Commit { : publish ? "Published successfully!" : "Pushed successfully!"; - return loading(startMsg, endMsg, repo.performPush(branch, publish, forceWithLease)).map(() => {}); + return loading(startMsg, endMsg, repo.performPush(branch, publish, forceWithLease)) + .chain((result) => + Future.concurrently< + Error, + { commit: repo.CommitMetadata; localBranch: string; upstream: Maybe; remoteUrl: string } + >({ + commit: repo.getCommitMetadata(), + localBranch: repo.getCurrentBranch(), + upstream: repo.getUpstream(), + remoteUrl: repo.getRemoteUrl() + }).map((parts) => ({ ...parts, range: result.range })) + ) + .map((metadata) => renderPushNote(metadata)); } interact(diff: string, message: string): Future { diff --git a/src/infra/git/repo.ts b/src/infra/git/repo.ts index a6adf23..81ec64f 100644 --- a/src/infra/git/repo.ts +++ b/src/infra/git/repo.ts @@ -1,11 +1,41 @@ -export { checkIsGitRepo, getStagedDiff, performCommit, performPush, getCurrentBranch, hasUpstream }; +export { + checkIsGitRepo, + getStagedDiff, + performCommit, + performPush, + getCurrentBranch, + hasUpstream, + getUpstream, + getCommitMetadata, + getRemoteUrl, + type CommitMetadata, + type PushResult, + type PushRange +}; import { Future } from "@/libs/future"; +import { Just, Nothing, type Maybe } from "@/libs/maybe"; import { spawn } from "node:child_process"; import { unlink, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; +type CommitMetadata = { + hash: string; + short: string; + subject: string; + authorName: string; + authorEmail: string; + date: Date; +}; + +type PushRange = { before: string; after: string }; + +type PushResult = { + output: string; + range: Maybe; +}; + const execGit = (args: string[]): Promise<{ stdout: string; stderr: string; exitCode: number }> => new Promise((resolve, reject) => { const proc = spawn("git", args, { stdio: ["pipe", "pipe", "pipe"] }); @@ -17,69 +47,84 @@ const execGit = (args: string[]): Promise<{ stdout: string; stderr: string; exit proc.on("close", (exitCode) => resolve({ stdout, stderr, exitCode: exitCode ?? 1 })); }); -const checkIsGitRepo = (): Future => - Future.attemptP(async () => { - const { exitCode } = await execGit(["rev-parse", "--is-inside-work-tree"]); - if (exitCode !== 0) throw new Error("Not a git repository"); - }); +const execGitChecked = (args: string[], fallbackMsg: string): Future => + Future.attemptP(() => execGit(args)).chain(({ stdout, stderr, exitCode }) => + exitCode !== 0 ? + Future.reject(new Error(stderr.trim() || stdout.trim() || fallbackMsg)) + : Future.resolve(stdout) + ); -const getStagedDiff = (): Future => - Future.attemptP(async () => { - const { stdout, stderr, exitCode } = await execGit(["diff", "--staged"]); +const parsePushRange = (output: string): Maybe => { + const m = output.match(/([0-9a-f]{7,40})\.\.([0-9a-f]{7,40})/); + if (!m) return Nothing(); + const [, before, after] = m; + return before && after ? Just({ before, after }) : Nothing(); +}; - if (exitCode !== 0) { - throw new Error(stderr.trim() || "Failed to get staged changes"); - } +const checkIsGitRepo = (): Future => + execGitChecked(["rev-parse", "--is-inside-work-tree"], "Not a git repository").map(() => {}); - if (!stdout.trim()) throw new Error("No staged changes found"); - return stdout; - }); +const getStagedDiff = (): Future => + execGitChecked(["diff", "--staged"], "Failed to get staged changes").chain((stdout) => + stdout.trim() ? + Future.resolve(stdout) + : Future.reject(new Error("No staged changes found")) + ); const performCommit = (message: string): Future => Future.attemptP(async () => { const tmpPath = join(tmpdir(), `commit-msg-${Date.now()}.txt`); await writeFile(tmpPath, message, "utf-8"); - - const { stdout, stderr, exitCode } = await execGit(["commit", "-F", tmpPath]); - + const res = await execGit(["commit", "-F", tmpPath]); await unlink(tmpPath).catch(() => {}); - - if (exitCode !== 0) { - throw new Error(stderr.trim() || stdout.trim() || "Commit failed"); - } - - const stats = stdout - .split("\n") - .filter((line) => !line.startsWith("[")) - .join("\n"); - - return "\n" + stats.trim() + "\n"; - }); - -const performPush = (branch?: string, publish = false, forceWithLease = false): Future => - Future.attemptP(async () => { - const args = publish && branch ? ["push", "--set-upstream", "origin", branch] : ["push"]; - - if (forceWithLease) args.push("--force-with-lease"); - - const { stdout, stderr, exitCode } = await execGit(args); - - if (exitCode !== 0) { - throw new Error(stderr.trim() || stdout.trim() || "Push failed"); - } - - return stdout || stderr; - }); + return res; + }).chain(({ stdout, stderr, exitCode }) => + exitCode !== 0 ? + Future.reject(new Error(stderr.trim() || stdout.trim() || "Commit failed")) + : Future.resolve( + "\n" + + stdout + .split("\n") + .filter((line) => !line.startsWith("[")) + .join("\n") + .trim() + + "\n" + ) + ); + +const performPush = (branch?: string, publish = false, forceWithLease = false): Future => { + const args = publish && branch ? ["push", "--set-upstream", "origin", branch] : ["push"]; + if (forceWithLease) args.push("--force-with-lease"); + return Future.attemptP(() => execGit(args)).chain(({ stdout, stderr, exitCode }) => + exitCode !== 0 ? + Future.reject(new Error(stderr.trim() || stdout.trim() || "Push failed")) + : Future.resolve({ + output: stdout + stderr, + range: parsePushRange(stdout + "\n" + stderr) + }) + ); +}; const getCurrentBranch = (): Future => - Future.attemptP(async () => { - const { stdout, exitCode } = await execGit(["rev-parse", "--abbrev-ref", "HEAD"]); - if (exitCode !== 0) throw new Error("Failed to get current branch"); - return stdout.trim(); - }); + execGitChecked(["rev-parse", "--abbrev-ref", "HEAD"], "Failed to get current branch").map((s) => s.trim()); const hasUpstream = (): Future => - Future.attemptP(async () => { - const { exitCode } = await execGit(["rev-parse", "--abbrev-ref", "@{u}"]); - return exitCode === 0; - }); + Future.attemptP(() => execGit(["rev-parse", "--abbrev-ref", "@{u}"])).map(({ exitCode }) => exitCode === 0); + +const getUpstream = (): Future> => + Future.attemptP(() => execGit(["rev-parse", "--abbrev-ref", "@{u}"])).map(({ stdout, exitCode }) => + exitCode !== 0 ? Nothing() : Just(stdout.trim()) + ); + +const getRemoteUrl = (remote: string = "origin"): Future => + execGitChecked(["remote", "get-url", remote], `Failed to read remote '${remote}' url`).map((s) => s.trim()); + +const getCommitMetadata = (ref: string = "HEAD"): Future => + execGitChecked(["log", "-1", `--format=%H%n%h%n%s%n%an%n%ae%n%aI`, ref], "Failed to read commit metadata").chain( + (stdout) => { + const [hash, short, subject, authorName, authorEmail, iso] = stdout.split("\n"); + return hash && short && subject !== undefined && authorName !== undefined && authorEmail !== undefined && iso ? + Future.resolve({ hash, short, subject, authorName, authorEmail, date: new Date(iso) }) + : Future.reject(new Error("Malformed git log output")); + } + ); diff --git a/src/infra/ui/push-note.ts b/src/infra/ui/push-note.ts new file mode 100644 index 0000000..e88990a --- /dev/null +++ b/src/infra/ui/push-note.ts @@ -0,0 +1,34 @@ +export { renderPushNote, type PushMetadata }; + +import * as p from "@clack/prompts"; + +import { type CommitMetadata, type PushRange } from "@/infra/git/repo"; +import { Just, type Maybe } from "@/libs/maybe"; + +type PushMetadata = { + commit: CommitMetadata; + localBranch: string; + upstream: Maybe; + remoteUrl: string; + range: Maybe; +}; + +const formatDate = (d: Date): string => d.toISOString().slice(0, 16).replace("T", " "); + +const renderPushNote = (m: PushMetadata): void => { + const branchLine = + m.upstream instanceof Just ? `branch ${m.localBranch} → ${m.upstream.value}` : `branch ${m.localBranch}`; + + const rangeLine = m.range instanceof Just ? [`range ${m.range.value.before}..${m.range.value.after}`] : []; + + const body = [ + `commit ${m.commit.short} ${m.commit.subject}`, + `author ${m.commit.authorName} <${m.commit.authorEmail}>`, + `date ${formatDate(m.commit.date)}`, + branchLine, + `remote ${m.remoteUrl}`, + ...rangeLine + ].join("\n"); + + p.note(body, "Pushed"); +}; From f179c85df152567ac22db73b080d487b9601b115 Mon Sep 17 00:00:00 2001 From: rafaeelricco Date: Tue, 21 Apr 2026 17:54:14 -0300 Subject: [PATCH 07/13] Show open pull request in push note - Add `src/infra/github/pr.ts` with `getOpenPullRequest` using `gh` CLI to look up the current branch's PR, classifying results as `found`, `unauthenticated`, or `unavailable`. - Extract `execBin` into `src/infra/shell.ts` and replace the private `execGit` helper in `repo.ts` with it; switch `performCommit` to `Future.bracket` for tmp file cleanup. - Extend `PushMetadata` with a `pr` field and render a PR line (or `gh auth login` hint) in `renderPushNote`. - Wire PR lookup into the `Commit` push flow so the push note surfaces the open PR URL. --- src/cli/commit.ts | 12 ++++++++++-- src/infra/git/repo.ts | 37 +++++++++++++------------------------ src/infra/github/pr.ts | 39 +++++++++++++++++++++++++++++++++++++++ src/infra/shell.ts | 20 ++++++++++++++++++++ src/infra/ui/push-note.ts | 21 +++++++++++++++++++-- 5 files changed, 101 insertions(+), 28 deletions(-) create mode 100644 src/infra/github/pr.ts create mode 100644 src/infra/shell.ts diff --git a/src/cli/commit.ts b/src/cli/commit.ts index ef14bd3..17037a7 100644 --- a/src/cli/commit.ts +++ b/src/cli/commit.ts @@ -1,6 +1,7 @@ export { Commit }; import * as p from "@clack/prompts"; +import * as pr from "@/infra/github/pr"; import * as repo from "@/infra/git/repo"; import { Future } from "@/libs/future"; @@ -92,12 +93,19 @@ class Commit { .chain((result) => Future.concurrently< Error, - { commit: repo.CommitMetadata; localBranch: string; upstream: Maybe; remoteUrl: string } + { + commit: repo.CommitMetadata; + localBranch: string; + upstream: Maybe; + remoteUrl: string; + pr: pr.PrLookup; + } >({ commit: repo.getCommitMetadata(), localBranch: repo.getCurrentBranch(), upstream: repo.getUpstream(), - remoteUrl: repo.getRemoteUrl() + remoteUrl: repo.getRemoteUrl(), + pr: pr.getOpenPullRequest() }).map((parts) => ({ ...parts, range: result.range })) ) .map((metadata) => renderPushNote(metadata)); diff --git a/src/infra/git/repo.ts b/src/infra/git/repo.ts index 81ec64f..db3ece0 100644 --- a/src/infra/git/repo.ts +++ b/src/infra/git/repo.ts @@ -15,7 +15,7 @@ export { import { Future } from "@/libs/future"; import { Just, Nothing, type Maybe } from "@/libs/maybe"; -import { spawn } from "node:child_process"; +import { execBin } from "@/infra/shell"; import { unlink, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; @@ -36,19 +36,8 @@ type PushResult = { range: Maybe; }; -const execGit = (args: string[]): Promise<{ stdout: string; stderr: string; exitCode: number }> => - new Promise((resolve, reject) => { - const proc = spawn("git", args, { stdio: ["pipe", "pipe", "pipe"] }); - let stdout = ""; - let stderr = ""; - proc.stdout.on("data", (d: Buffer) => (stdout += d.toString())); - proc.stderr.on("data", (d: Buffer) => (stderr += d.toString())); - proc.on("error", reject); - proc.on("close", (exitCode) => resolve({ stdout, stderr, exitCode: exitCode ?? 1 })); - }); - const execGitChecked = (args: string[], fallbackMsg: string): Future => - Future.attemptP(() => execGit(args)).chain(({ stdout, stderr, exitCode }) => + execBin("git", args).chain(({ stdout, stderr, exitCode }) => exitCode !== 0 ? Future.reject(new Error(stderr.trim() || stdout.trim() || fallbackMsg)) : Future.resolve(stdout) @@ -71,14 +60,13 @@ const getStagedDiff = (): Future => : Future.reject(new Error("No staged changes found")) ); -const performCommit = (message: string): Future => - Future.attemptP(async () => { - const tmpPath = join(tmpdir(), `commit-msg-${Date.now()}.txt`); - await writeFile(tmpPath, message, "utf-8"); - const res = await execGit(["commit", "-F", tmpPath]); - await unlink(tmpPath).catch(() => {}); - return res; - }).chain(({ stdout, stderr, exitCode }) => +const performCommit = (message: string): Future => { + const tmpPath = join(tmpdir(), `commit-msg-${Date.now()}.txt`); + return Future.bracket( + Future.attemptP(() => writeFile(tmpPath, message, "utf-8")), + () => Future.attemptP(() => unlink(tmpPath).catch(() => {})), + () => execBin("git", ["commit", "-F", tmpPath]) + ).chain(({ stdout, stderr, exitCode }) => exitCode !== 0 ? Future.reject(new Error(stderr.trim() || stdout.trim() || "Commit failed")) : Future.resolve( @@ -91,11 +79,12 @@ const performCommit = (message: string): Future => "\n" ) ); +}; const performPush = (branch?: string, publish = false, forceWithLease = false): Future => { const args = publish && branch ? ["push", "--set-upstream", "origin", branch] : ["push"]; if (forceWithLease) args.push("--force-with-lease"); - return Future.attemptP(() => execGit(args)).chain(({ stdout, stderr, exitCode }) => + return execBin("git", args).chain(({ stdout, stderr, exitCode }) => exitCode !== 0 ? Future.reject(new Error(stderr.trim() || stdout.trim() || "Push failed")) : Future.resolve({ @@ -109,10 +98,10 @@ const getCurrentBranch = (): Future => execGitChecked(["rev-parse", "--abbrev-ref", "HEAD"], "Failed to get current branch").map((s) => s.trim()); const hasUpstream = (): Future => - Future.attemptP(() => execGit(["rev-parse", "--abbrev-ref", "@{u}"])).map(({ exitCode }) => exitCode === 0); + execBin("git", ["rev-parse", "--abbrev-ref", "@{u}"]).map(({ exitCode }) => exitCode === 0); const getUpstream = (): Future> => - Future.attemptP(() => execGit(["rev-parse", "--abbrev-ref", "@{u}"])).map(({ stdout, exitCode }) => + execBin("git", ["rev-parse", "--abbrev-ref", "@{u}"]).map(({ stdout, exitCode }) => exitCode !== 0 ? Nothing() : Just(stdout.trim()) ); diff --git a/src/infra/github/pr.ts b/src/infra/github/pr.ts new file mode 100644 index 0000000..a85f50f --- /dev/null +++ b/src/infra/github/pr.ts @@ -0,0 +1,39 @@ +export { getOpenPullRequest, type PullRequest, type PrLookup }; + +import * as repo from "@/infra/git/repo"; + +import { Future } from "@/libs/future"; +import { execBin } from "@/infra/shell"; + +type PullRequest = { url: string; number: number }; + +type PrLookup = { type: "found"; pr: PullRequest } | { type: "unauthenticated" } | { type: "unavailable" }; + +const GITHUB_HOST_RE = /github\.com[:/]/; +const GH_UNAUTH_RE = /not logged into|gh auth login|authentication required/i; + +const parsePrJson = (stdout: string): PrLookup => { + try { + const parsed = JSON.parse(stdout) as { url?: unknown; number?: unknown }; + return typeof parsed.url === "string" && typeof parsed.number === "number" ? + { type: "found", pr: { url: parsed.url, number: parsed.number } } + : { type: "unavailable" }; + } catch { + return { type: "unavailable" }; + } +}; + +const classifyFailure = (stderr: string): PrLookup => + GH_UNAUTH_RE.test(stderr) ? { type: "unauthenticated" } : { type: "unavailable" }; + +const getOpenPullRequest = (): Future => + repo + .getRemoteUrl() + .chain((remoteUrl) => + !GITHUB_HOST_RE.test(remoteUrl) ? + Future.resolve({ type: "unavailable" }) + : execBin("gh", ["pr", "view", "--json", "url,number"]).map(({ stdout, stderr, exitCode }) => + exitCode !== 0 ? classifyFailure(stderr) : parsePrJson(stdout) + ) + ) + .chainRej((): Future => Future.resolve({ type: "unavailable" })); diff --git a/src/infra/shell.ts b/src/infra/shell.ts new file mode 100644 index 0000000..e4e71dd --- /dev/null +++ b/src/infra/shell.ts @@ -0,0 +1,20 @@ +export { execBin, type ExecResult }; + +import { Future } from "@/libs/future"; +import { spawn } from "node:child_process"; + +type ExecResult = { stdout: string; stderr: string; exitCode: number }; + +const execBin = (bin: string, args: string[]): Future => + Future.create((reject, resolve) => { + const proc = spawn(bin, args, { stdio: ["pipe", "pipe", "pipe"] }); + let stdout = ""; + let stderr = ""; + proc.stdout.on("data", (d: Buffer) => (stdout += d.toString())); + proc.stderr.on("data", (d: Buffer) => (stderr += d.toString())); + proc.on("error", (err) => reject(err instanceof Error ? err : new Error(String(err)))); + proc.on("close", (exitCode) => resolve({ stdout, stderr, exitCode: exitCode ?? 1 })); + return () => { + proc.kill(); + }; + }); diff --git a/src/infra/ui/push-note.ts b/src/infra/ui/push-note.ts index e88990a..d7eb237 100644 --- a/src/infra/ui/push-note.ts +++ b/src/infra/ui/push-note.ts @@ -2,8 +2,10 @@ export { renderPushNote, type PushMetadata }; import * as p from "@clack/prompts"; -import { type CommitMetadata, type PushRange } from "@/infra/git/repo"; +import type { CommitMetadata, PushRange } from "@/infra/git/repo"; +import type { PrLookup } from "@/infra/github/pr"; import { Just, type Maybe } from "@/libs/maybe"; +import { absurd } from "@/libs/types"; type PushMetadata = { commit: CommitMetadata; @@ -11,10 +13,24 @@ type PushMetadata = { upstream: Maybe; remoteUrl: string; range: Maybe; + pr: PrLookup; }; const formatDate = (d: Date): string => d.toISOString().slice(0, 16).replace("T", " "); +const renderPrLine = (lookup: PrLookup): string[] => { + switch (lookup.type) { + case "found": + return [`pr #${lookup.pr.number} ${lookup.pr.url}`]; + case "unauthenticated": + return [`pr Tip: run 'gh auth login' to show open PR in the current branch...`]; + case "unavailable": + return []; + default: + return absurd(lookup, "PrLookup"); + } +}; + const renderPushNote = (m: PushMetadata): void => { const branchLine = m.upstream instanceof Just ? `branch ${m.localBranch} → ${m.upstream.value}` : `branch ${m.localBranch}`; @@ -27,7 +43,8 @@ const renderPushNote = (m: PushMetadata): void => { `date ${formatDate(m.commit.date)}`, branchLine, `remote ${m.remoteUrl}`, - ...rangeLine + ...rangeLine, + ...renderPrLine(m.pr) ].join("\n"); p.note(body, "Pushed"); From bc3c26980d4d8255656e74090e163a10b54eae3f Mon Sep 17 00:00:00 2001 From: rafaeelricco Date: Tue, 21 Apr 2026 18:04:31 -0300 Subject: [PATCH 08/13] Swallow push note errors and inline render call --- src/cli/commit.ts | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/cli/commit.ts b/src/cli/commit.ts index 17037a7..cc14185 100644 --- a/src/cli/commit.ts +++ b/src/cli/commit.ts @@ -89,26 +89,26 @@ class Commit { : publish ? "Published successfully!" : "Pushed successfully!"; - return loading(startMsg, endMsg, repo.performPush(branch, publish, forceWithLease)) - .chain((result) => - Future.concurrently< - Error, - { - commit: repo.CommitMetadata; - localBranch: string; - upstream: Maybe; - remoteUrl: string; - pr: pr.PrLookup; - } - >({ - commit: repo.getCommitMetadata(), - localBranch: repo.getCurrentBranch(), - upstream: repo.getUpstream(), - remoteUrl: repo.getRemoteUrl(), - pr: pr.getOpenPullRequest() - }).map((parts) => ({ ...parts, range: result.range })) - ) - .map((metadata) => renderPushNote(metadata)); + return loading(startMsg, endMsg, repo.performPush(branch, publish, forceWithLease)).chain((result) => + Future.concurrently< + Error, + { + commit: repo.CommitMetadata; + localBranch: string; + upstream: Maybe; + remoteUrl: string; + pr: pr.PrLookup; + } + >({ + commit: repo.getCommitMetadata(), + localBranch: repo.getCurrentBranch(), + upstream: repo.getUpstream(), + remoteUrl: repo.getRemoteUrl(), + pr: pr.getOpenPullRequest() + }) + .map((parts) => renderPushNote({ ...parts, range: result.range })) + .chainRej(() => Future.resolve(undefined)) + ); } interact(diff: string, message: string): Future { From afd198accaf78734721382eb8e68831e2af337f0 Mon Sep 17 00:00:00 2001 From: rafaeelricco Date: Tue, 21 Apr 2026 18:05:05 -0300 Subject: [PATCH 09/13] Remove CLAUDE.md development guidelines file --- CLAUDE.md | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 10c1d84..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -description: Use Node.js with tsx/tsup for development and builds. -globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json" -alwaysApply: false ---- - -Default to using Node.js with tsx for TypeScript execution and tsup for bundling. - -- Use `tsx ` instead of `ts-node ` for running TypeScript files -- Use `pnpm run build` (tsup) for bundling -- Use `pnpm install` for installing dependencies -- Use `pnpm run