diff --git a/package-lock.json b/package-lock.json index 8d960a8e..75f17302 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@modelcontextprotocol/sdk": "^1.23.4", "dotenv": "^16.4.7", "openid-client": "^6.8.1", - "xero-node": "^13.3.0", + "xero-node": "^15.0.1", "zod": "3.25" }, "bin": { @@ -31,6 +31,29 @@ "vitest": "^4.1.5" } }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -43,9 +66,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -75,9 +98,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { @@ -85,15 +108,15 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", - "minimatch": "^3.1.2" + "minimatch": "^3.1.5" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -126,20 +149,20 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.4", + "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { @@ -150,9 +173,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -187,9 +210,9 @@ "license": "MIT" }, "node_modules/@eslint/js": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", - "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "dev": true, "license": "MIT", "engines": { @@ -236,41 +259,41 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", "dev": true, "license": "Apache-2.0", "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -288,9 +311,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -309,9 +332,9 @@ "license": "MIT" }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", - "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", "license": "MIT", "dependencies": { "@hono/node-server": "^1.19.9", @@ -368,9 +391,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.132.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.132.0.tgz", - "integrity": "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==", + "version": "0.133.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", + "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", "dev": true, "license": "MIT", "funding": { @@ -378,9 +401,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz", - "integrity": "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", + "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", "cpu": [ "arm64" ], @@ -395,9 +418,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.2.tgz", - "integrity": "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", + "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", "cpu": [ "arm64" ], @@ -412,9 +435,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.2.tgz", - "integrity": "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", + "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", "cpu": [ "x64" ], @@ -429,9 +452,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.2.tgz", - "integrity": "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", + "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", "cpu": [ "x64" ], @@ -446,9 +469,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.2.tgz", - "integrity": "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", + "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", "cpu": [ "arm" ], @@ -463,9 +486,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.2.tgz", - "integrity": "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", + "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", "cpu": [ "arm64" ], @@ -480,9 +503,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.2.tgz", - "integrity": "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", + "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", "cpu": [ "arm64" ], @@ -497,9 +520,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.2.tgz", - "integrity": "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", + "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", "cpu": [ "ppc64" ], @@ -514,9 +537,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.2.tgz", - "integrity": "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", + "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", "cpu": [ "s390x" ], @@ -531,9 +554,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.2.tgz", - "integrity": "sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", "cpu": [ "x64" ], @@ -548,9 +571,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.2.tgz", - "integrity": "sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", + "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", "cpu": [ "x64" ], @@ -565,9 +588,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.2.tgz", - "integrity": "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", + "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", "cpu": [ "arm64" ], @@ -582,9 +605,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.2.tgz", - "integrity": "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", + "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", "cpu": [ "wasm32" ], @@ -601,9 +624,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.2.tgz", - "integrity": "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", + "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", "cpu": [ "arm64" ], @@ -618,9 +641,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.2.tgz", - "integrity": "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", + "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", "cpu": [ "x64" ], @@ -678,9 +701,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, @@ -692,32 +715,30 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.13.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", - "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", + "version": "22.19.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz", + "integrity": "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "undici-types": "~6.20.0" + "undici-types": "~6.21.0" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.1.tgz", - "integrity": "sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz", + "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.48.1", - "@typescript-eslint/type-utils": "8.48.1", - "@typescript-eslint/utils": "8.48.1", - "@typescript-eslint/visitor-keys": "8.48.1", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/type-utils": "8.60.1", + "@typescript-eslint/utils": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -727,9 +748,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.48.1", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "@typescript-eslint/parser": "^8.60.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -743,18 +764,17 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.48.1.tgz", - "integrity": "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz", + "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.48.1", - "@typescript-eslint/types": "8.48.1", - "@typescript-eslint/typescript-estree": "8.48.1", - "@typescript-eslint/visitor-keys": "8.48.1", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -764,20 +784,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.48.1.tgz", - "integrity": "sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz", + "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.48.1", - "@typescript-eslint/types": "^8.48.1", - "debug": "^4.3.4" + "@typescript-eslint/tsconfig-utils": "^8.60.1", + "@typescript-eslint/types": "^8.60.1", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -787,18 +807,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.48.1.tgz", - "integrity": "sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz", + "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.48.1", - "@typescript-eslint/visitor-keys": "8.48.1" + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -809,9 +829,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.1.tgz", - "integrity": "sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz", + "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==", "dev": true, "license": "MIT", "engines": { @@ -822,21 +842,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.48.1.tgz", - "integrity": "sha512-1jEop81a3LrJQLTf/1VfPQdhIY4PlGDBc/i67EVWObrtvcziysbLN3oReexHOM6N3jyXgCrkBsZpqwH0hiDOQg==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz", + "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.48.1", - "@typescript-eslint/typescript-estree": "8.48.1", - "@typescript-eslint/utils": "8.48.1", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/utils": "8.60.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -846,14 +866,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.48.1.tgz", - "integrity": "sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz", + "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==", "dev": true, "license": "MIT", "engines": { @@ -865,21 +885,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.1.tgz", - "integrity": "sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz", + "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.48.1", - "@typescript-eslint/tsconfig-utils": "8.48.1", - "@typescript-eslint/types": "8.48.1", - "@typescript-eslint/visitor-keys": "8.48.1", - "debug": "^4.3.4", - "minimatch": "^9.0.4", - "semver": "^7.6.0", + "@typescript-eslint/project-service": "8.60.1", + "@typescript-eslint/tsconfig-utils": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -889,46 +909,59 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.2" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.48.1.tgz", - "integrity": "sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz", + "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.48.1", - "@typescript-eslint/types": "8.48.1", - "@typescript-eslint/typescript-estree": "8.48.1" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -938,19 +971,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.1.tgz", - "integrity": "sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz", + "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.48.1", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.60.1", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -960,17 +993,30 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@vitest/expect": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.7.tgz", - "integrity": "sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz", + "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.7", - "@vitest/utils": "4.1.7", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -979,13 +1025,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.7.tgz", - "integrity": "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", + "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.7", + "@vitest/spy": "4.1.8", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -1006,9 +1052,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.7.tgz", - "integrity": "sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", + "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", "dev": true, "license": "MIT", "dependencies": { @@ -1019,13 +1065,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.7.tgz", - "integrity": "sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz", + "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.7", + "@vitest/utils": "4.1.8", "pathe": "^2.0.3" }, "funding": { @@ -1033,14 +1079,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.7.tgz", - "integrity": "sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", + "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.7", - "@vitest/utils": "4.1.7", + "@vitest/pretty-format": "4.1.8", + "@vitest/utils": "4.1.8", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -1049,9 +1095,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.7.tgz", - "integrity": "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz", + "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", "dev": true, "license": "MIT", "funding": { @@ -1059,13 +1105,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.7.tgz", - "integrity": "sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", + "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.7", + "@vitest/pretty-format": "4.1.8", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -1087,12 +1133,11 @@ } }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1110,10 +1155,22 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -1183,13 +1240,14 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", - "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.17.0.tgz", + "integrity": "sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.11", + "follow-redirects": "^1.16.0", "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, @@ -1225,9 +1283,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", "dev": true, "license": "MIT", "dependencies": { @@ -1350,9 +1408,9 @@ "license": "MIT" }, "node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", "engines": { "node": ">=18" @@ -1397,9 +1455,9 @@ } }, "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", "license": "MIT", "dependencies": { "object-assign": "^4", @@ -1407,6 +1465,10 @@ }, "engines": { "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/cross-spawn": { @@ -1542,9 +1604,9 @@ "license": "MIT" }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -1588,26 +1650,25 @@ } }, "node_modules/eslint": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", - "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", + "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.1", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "ajv": "^6.12.4", + "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", @@ -1626,7 +1687,7 @@ "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -1695,9 +1756,9 @@ } }, "node_modules/eslint/node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -1737,9 +1798,9 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -1802,21 +1863,21 @@ } }, "node_modules/eventsource": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.5.tgz", - "integrity": "sha512-LT/5J605bx5SNyE+ITBDiM3FxffBiq9un7Vx0EwMDM3vg8sWKx/tO2zC+LMqZ+smAM0F2hblaDZUVZF0te2pSw==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", "license": "MIT", "dependencies": { - "eventsource-parser": "^3.0.0" + "eventsource-parser": "^3.0.1" }, "engines": { "node": ">=18.0.0" } }, "node_modules/eventsource-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.0.tgz", - "integrity": "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz", + "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -1837,7 +1898,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -1877,12 +1937,12 @@ } }, "node_modules/express-rate-limit": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.2.tgz", - "integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", + "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", "license": "MIT", "dependencies": { - "ip-address": "10.1.0" + "ip-address": "^10.2.0" }, "engines": { "node": ">= 16" @@ -1915,9 +1975,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "funding": [ { "type": "github", @@ -2167,7 +2227,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { @@ -2223,13 +2283,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2268,9 +2321,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -2280,11 +2333,10 @@ } }, "node_modules/hono": { - "version": "4.12.14", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.14.tgz", - "integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==", + "version": "4.12.23", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.23.tgz", + "integrity": "sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==", "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -2309,6 +2361,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/iconv-lite": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", @@ -2391,9 +2456,9 @@ } }, "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", "license": "MIT", "engines": { "node": ">= 12" @@ -2409,13 +2474,13 @@ } }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -2460,19 +2525,29 @@ "license": "ISC" }, "node_modules/jose": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", - "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", + "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" } }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -2957,9 +3032,9 @@ } }, "node_modules/oauth4webapi": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.3.tgz", - "integrity": "sha512-pQ5BsX3QRTgnt5HxgHwgunIRaDXBdkT23tf8dfzmtTIL2LTpdmxgbpbBm0VgFWAIDlezQvQCTgnVIUmHupXHxw==", + "version": "3.8.6", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.6.tgz", + "integrity": "sha512-iwemM91xz8nryHti2yTmg5fhyEMVOkOXwHNqbvcATjyajb5oQxCQzrNOA6uElRHuMhQQTKUyFKV9y/CNyg25BQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -2996,15 +3071,18 @@ } }, "node_modules/obug": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", - "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.2.tgz", + "integrity": "sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" ], - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } }, "node_modules/oidc-token-hash": { "version": "5.2.0", @@ -3037,13 +3115,13 @@ } }, "node_modules/openid-client": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.8.1.tgz", - "integrity": "sha512-VoYT6enBo6Vj2j3Q5Ec0AezS+9YGzQo1f5Xc42lreMGlfP4ljiXPKVDvCADh+XHCV/bqPu/wWSiCVXbJKvrODw==", + "version": "6.8.4", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.8.4.tgz", + "integrity": "sha512-QSw0BA08piujetEwfZsHoTrDpMEha7GDZDicQqVwX4u0ChCjefvjDB++TZ8BTg76UpwhzIQgdvvfgfl3HpCSAw==", "license": "MIT", "dependencies": { - "jose": "^6.1.0", - "oauth4webapi": "^3.8.2" + "jose": "^6.2.2", + "oauth4webapi": "^3.8.5" }, "funding": { "url": "https://github.com/sponsors/panva" @@ -3187,7 +3265,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -3292,9 +3369,9 @@ } }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -3352,13 +3429,14 @@ } }, "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -3383,13 +3461,13 @@ } }, "node_modules/rolldown": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz", - "integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", + "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.132.0", + "@oxc-project/types": "=0.133.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -3399,21 +3477,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.2", - "@rolldown/binding-darwin-arm64": "1.0.2", - "@rolldown/binding-darwin-x64": "1.0.2", - "@rolldown/binding-freebsd-x64": "1.0.2", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.2", - "@rolldown/binding-linux-arm64-gnu": "1.0.2", - "@rolldown/binding-linux-arm64-musl": "1.0.2", - "@rolldown/binding-linux-ppc64-gnu": "1.0.2", - "@rolldown/binding-linux-s390x-gnu": "1.0.2", - "@rolldown/binding-linux-x64-gnu": "1.0.2", - "@rolldown/binding-linux-x64-musl": "1.0.2", - "@rolldown/binding-openharmony-arm64": "1.0.2", - "@rolldown/binding-wasm32-wasi": "1.0.2", - "@rolldown/binding-win32-arm64-msvc": "1.0.2", - "@rolldown/binding-win32-x64-msvc": "1.0.2" + "@rolldown/binding-android-arm64": "1.0.3", + "@rolldown/binding-darwin-arm64": "1.0.3", + "@rolldown/binding-darwin-x64": "1.0.3", + "@rolldown/binding-freebsd-x64": "1.0.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.3", + "@rolldown/binding-linux-arm64-musl": "1.0.3", + "@rolldown/binding-linux-ppc64-gnu": "1.0.3", + "@rolldown/binding-linux-s390x-gnu": "1.0.3", + "@rolldown/binding-linux-x64-gnu": "1.0.3", + "@rolldown/binding-linux-x64-musl": "1.0.3", + "@rolldown/binding-openharmony-arm64": "1.0.3", + "@rolldown/binding-wasm32-wasi": "1.0.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.3", + "@rolldown/binding-win32-x64-msvc": "1.0.3" } }, "node_modules/router": { @@ -3439,9 +3517,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", + "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", "dev": true, "license": "ISC", "bin": { @@ -3578,13 +3656,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -3717,9 +3795,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.2.tgz", - "integrity": "sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", "dev": true, "license": "MIT", "engines": { @@ -3727,9 +3805,9 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { @@ -3763,9 +3841,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -3797,17 +3875,34 @@ } }, "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", "dependencies": { - "content-type": "^1.0.5", + "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/typescript": { @@ -3816,7 +3911,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3826,16 +3920,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.48.1.tgz", - "integrity": "sha512-FbOKN1fqNoXp1hIl5KYpObVrp0mCn+CLgn479nmu2IsRMrx2vyv74MmsBLVlhg8qVwNFGbXSp8fh1zp8pEoC2A==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.1.tgz", + "integrity": "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.48.1", - "@typescript-eslint/parser": "8.48.1", - "@typescript-eslint/typescript-estree": "8.48.1", - "@typescript-eslint/utils": "8.48.1" + "@typescript-eslint/eslint-plugin": "8.60.1", + "@typescript-eslint/parser": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/utils": "8.60.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3845,14 +3939,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -3885,18 +3979,17 @@ } }, "node_modules/vite": { - "version": "8.0.14", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz", - "integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==", + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", + "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", - "rolldown": "1.0.2", - "tinyglobby": "^0.2.16" + "rolldown": "1.0.3", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" @@ -3964,19 +4057,19 @@ } }, "node_modules/vitest": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.7.tgz", - "integrity": "sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz", + "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.7", - "@vitest/mocker": "4.1.7", - "@vitest/pretty-format": "4.1.7", - "@vitest/runner": "4.1.7", - "@vitest/snapshot": "4.1.7", - "@vitest/spy": "4.1.7", - "@vitest/utils": "4.1.7", + "@vitest/expect": "4.1.8", + "@vitest/mocker": "4.1.8", + "@vitest/pretty-format": "4.1.8", + "@vitest/runner": "4.1.8", + "@vitest/snapshot": "4.1.8", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -4004,12 +4097,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.7", - "@vitest/browser-preview": "4.1.7", - "@vitest/browser-webdriverio": "4.1.7", - "@vitest/coverage-istanbul": "4.1.7", - "@vitest/coverage-v8": "4.1.7", - "@vitest/ui": "4.1.7", + "@vitest/browser-playwright": "4.1.8", + "@vitest/browser-preview": "4.1.8", + "@vitest/browser-webdriverio": "4.1.8", + "@vitest/coverage-istanbul": "4.1.8", + "@vitest/coverage-v8": "4.1.8", + "@vitest/ui": "4.1.8", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -4102,9 +4195,9 @@ "license": "ISC" }, "node_modules/xero-node": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/xero-node/-/xero-node-13.3.0.tgz", - "integrity": "sha512-D7qZrOPkN1crFx8VRnoe7UI403t/uhYugggF1jQHlUTYBNiEeVYJVPCQkYdYcy0yD8sxTEsdOCfw9CSZsd2Tvw==", + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/xero-node/-/xero-node-15.0.1.tgz", + "integrity": "sha512-hqx7jA8iB5Al3G7fPA4qOHJUX+FJl7yChJEmlw2E7xWf41Md30sj7NFlvabDxPqVBK+Wm/yn/xlRTlYRDCB/6A==", "license": "MIT", "dependencies": { "axios": "^1.7.7", @@ -4159,18 +4252,17 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } }, "node_modules/zod-to-json-schema": { - "version": "3.25.1", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", - "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", "license": "ISC", "peerDependencies": { - "zod": "^3.25 || ^4" + "zod": "^3.25.28 || ^4" } } } diff --git a/package.json b/package.json index 590fcb76..f5e913dd 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@modelcontextprotocol/sdk": "^1.23.4", "dotenv": "^16.4.7", "openid-client": "^6.8.1", - "xero-node": "^13.3.0", + "xero-node": "^15.0.1", "zod": "3.25" }, "devDependencies": { diff --git a/src/clients/xero-client.ts b/src/clients/xero-client.ts index 2a0097c3..046199c8 100644 --- a/src/clients/xero-client.ts +++ b/src/clients/xero-client.ts @@ -6,6 +6,7 @@ import { TokenSet, XeroClient, } from "xero-node"; +import { PayrollAuV2Api } from "xero-node/dist/gen/api/payrollAUV2Api.js"; import { ensureError } from "../helpers/ensure-error.js"; @@ -20,9 +21,20 @@ if (!bearer_token && (!client_id || !client_secret)) { throw Error("Environment Variables not set - please check your .env file"); } +export type PayrollRegion = "AU" | "NZ" | "UK"; + abstract class MCPXeroClient extends XeroClient { public tenantId: string; private shortCode: string; + private _region: PayrollRegion | null = null; + private _payrollAUV2Api: PayrollAuV2Api | null = null; + + get payrollAUV2Api(): PayrollAuV2Api { + if (!this._payrollAUV2Api) { + this._payrollAUV2Api = new PayrollAuV2Api(); + } + return this._payrollAUV2Api; + } protected constructor(config?: IXeroClientConfig) { super(config); @@ -41,7 +53,7 @@ abstract class MCPXeroClient extends XeroClient { return this.tenants; } - private async getOrganisation(): Promise { + public async getOrganisation(): Promise { await this.authenticate(); const organisationResponse = await this.accountingApi.getOrganisations( @@ -72,6 +84,17 @@ abstract class MCPXeroClient extends XeroClient { } return this.shortCode; } + + public async getRegion(): Promise { + if (!this._region) { + const org = await this.getOrganisation(); + const code = String(org.countryCode ?? ""); + if (code === "AU") this._region = "AU"; + else if (code === "GB") this._region = "UK"; + else this._region = "NZ"; + } + return this._region; + } } class CustomConnectionsXeroClient extends MCPXeroClient { @@ -200,6 +223,8 @@ class CustomConnectionsXeroClient extends MCPXeroClient { expires_in: tokenResponse.expires_in, token_type: tokenResponse.token_type, }); + + this.payrollAUV2Api.accessToken = tokenResponse.access_token ?? ""; } } @@ -216,6 +241,8 @@ class BearerTokenXeroClient extends MCPXeroClient { access_token: this.bearerToken, }); + this.payrollAUV2Api.accessToken = this.bearerToken; + await this.updateTenants(); } } diff --git a/src/handlers/__tests__/add-timesheet-line.handler.test.ts b/src/handlers/__tests__/add-timesheet-line.handler.test.ts new file mode 100644 index 00000000..dbeb04c8 --- /dev/null +++ b/src/handlers/__tests__/add-timesheet-line.handler.test.ts @@ -0,0 +1,112 @@ +import { vi, describe, it, expect, beforeEach } from "vitest"; + +const { mockXeroClient } = vi.hoisted(() => ({ + mockXeroClient: { + authenticate: vi.fn(), + getRegion: vi.fn(), + tenantId: "test-tenant-id", + payrollAUV2Api: { createTimesheetLine: vi.fn() }, + payrollNZApi: { createTimesheetLine: vi.fn() }, + payrollUKApi: { createTimesheetLine: vi.fn() }, + }, +})); + +vi.mock("../../clients/xero-client.js", () => ({ + xeroClient: mockXeroClient, +})); + +import { updateXeroPayrollTimesheetAddLine } from "../update-xero-payroll-timesheet-add-line.handler.js"; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("updateXeroPayrollTimesheetAddLine", () => { + describe("AU region", () => { + beforeEach(() => { + mockXeroClient.getRegion.mockResolvedValue("AU"); + }); + + it("calls payrollAUV2Api.createTimesheetLine", async () => { + mockXeroClient.payrollAUV2Api.createTimesheetLine.mockResolvedValue({ + body: { timesheetLine: { timesheetLineID: "line-1", earningsRateID: "rate-1" } }, + }); + + const result = await updateXeroPayrollTimesheetAddLine({ + timesheetID: "ts-1", + earningsRateID: "rate-1", + numberOfUnits: 8, + date: "2024-01-01", + }); + + expect(mockXeroClient.payrollAUV2Api.createTimesheetLine).toHaveBeenCalledWith( + "test-tenant-id", + "ts-1", + expect.objectContaining({ + earningsRateID: "rate-1", + numberOfUnits: 8, + date: "2024-01-01", + }), + ); + expect(result.isError).toBe(false); + }); + }); + + describe("NZ region", () => { + beforeEach(() => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + }); + + it("calls payrollNZApi.createTimesheetLine with date and single numberOfUnits", async () => { + mockXeroClient.payrollNZApi.createTimesheetLine.mockResolvedValue({ + body: { timesheetLine: { timesheetLineID: "line-1" } }, + }); + + await updateXeroPayrollTimesheetAddLine({ + timesheetID: "ts-1", + earningsRateID: "rate-1", + numberOfUnits: 8, + date: "2024-01-01", + }); + + const line = mockXeroClient.payrollNZApi.createTimesheetLine.mock.calls[0][2]; + expect(line.earningsRateID).toBe("rate-1"); + expect(line.numberOfUnits).toBe(8); + expect(line.date).toBe("2024-01-01"); + }); + }); + + describe("UK region", () => { + it("calls payrollUKApi.createTimesheetLine", async () => { + mockXeroClient.getRegion.mockResolvedValue("UK"); + mockXeroClient.payrollUKApi.createTimesheetLine.mockResolvedValue({ + body: { timesheetLine: { timesheetLineID: "line-1" } }, + }); + + await updateXeroPayrollTimesheetAddLine({ + timesheetID: "ts-1", + earningsRateID: "rate-1", + numberOfUnits: 8, + date: "2024-01-01", + }); + + expect(mockXeroClient.payrollUKApi.createTimesheetLine).toHaveBeenCalled(); + expect(mockXeroClient.payrollNZApi.createTimesheetLine).not.toHaveBeenCalled(); + }); + }); + + it("returns error response on API failure", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.createTimesheetLine.mockRejectedValue(new Error("Invalid line")); + + const result = await updateXeroPayrollTimesheetAddLine({ + timesheetID: "ts-1", + earningsRateID: "rate-1", + numberOfUnits: 8, + date: "2024-01-01", + }); + + expect(result.isError).toBe(true); + expect(result.error).toBe("Invalid line"); + }); +}); diff --git a/src/handlers/__tests__/approve-xero-payroll-timesheet.handler.test.ts b/src/handlers/__tests__/approve-xero-payroll-timesheet.handler.test.ts new file mode 100644 index 00000000..e909f427 --- /dev/null +++ b/src/handlers/__tests__/approve-xero-payroll-timesheet.handler.test.ts @@ -0,0 +1,78 @@ +import { vi, describe, it, expect, beforeEach } from "vitest"; + +const { mockXeroClient } = vi.hoisted(() => ({ + mockXeroClient: { + authenticate: vi.fn(), + getRegion: vi.fn(), + tenantId: "test-tenant-id", + payrollAUV2Api: { approveTimesheet: vi.fn() }, + payrollNZApi: { approveTimesheet: vi.fn() }, + payrollUKApi: { approveTimesheet: vi.fn() }, + }, +})); + +vi.mock("../../clients/xero-client.js", () => ({ + xeroClient: mockXeroClient, +})); + +import { approveXeroPayrollTimesheet } from "../approve-xero-payroll-timesheet.handler.js"; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("approveXeroPayrollTimesheet", () => { + describe("AU region", () => { + beforeEach(() => { + mockXeroClient.getRegion.mockResolvedValue("AU"); + }); + + it("calls payrollAUV2Api.approveTimesheet", async () => { + mockXeroClient.payrollAUV2Api.approveTimesheet.mockResolvedValue({ + body: { timesheet: { timesheetID: "ts-1", status: "Approved" } }, + }); + + const result = await approveXeroPayrollTimesheet("ts-1"); + + expect(mockXeroClient.payrollAUV2Api.approveTimesheet).toHaveBeenCalledWith("test-tenant-id", "ts-1"); + expect(result.isError).toBe(false); + expect(result.result?.timesheetID).toBe("ts-1"); + }); + }); + + describe("NZ region", () => { + it("calls payrollNZApi.approveTimesheet", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.approveTimesheet.mockResolvedValue({ + body: { timesheet: { timesheetID: "ts-1" } }, + }); + + await approveXeroPayrollTimesheet("ts-1"); + + expect(mockXeroClient.payrollNZApi.approveTimesheet).toHaveBeenCalledWith("test-tenant-id", "ts-1"); + }); + }); + + describe("UK region", () => { + it("calls payrollUKApi.approveTimesheet", async () => { + mockXeroClient.getRegion.mockResolvedValue("UK"); + mockXeroClient.payrollUKApi.approveTimesheet.mockResolvedValue({ + body: { timesheet: { timesheetID: "ts-1" } }, + }); + + await approveXeroPayrollTimesheet("ts-1"); + + expect(mockXeroClient.payrollUKApi.approveTimesheet).toHaveBeenCalledWith("test-tenant-id", "ts-1"); + expect(mockXeroClient.payrollNZApi.approveTimesheet).not.toHaveBeenCalled(); + }); + }); + + it("returns error response on API failure", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.approveTimesheet.mockRejectedValue(new Error("Already approved")); + + const result = await approveXeroPayrollTimesheet("ts-1"); + expect(result.isError).toBe(true); + expect(result.error).toBe("Already approved"); + }); +}); diff --git a/src/handlers/__tests__/create-xero-payroll-timesheet.handler.test.ts b/src/handlers/__tests__/create-xero-payroll-timesheet.handler.test.ts new file mode 100644 index 00000000..d90796b7 --- /dev/null +++ b/src/handlers/__tests__/create-xero-payroll-timesheet.handler.test.ts @@ -0,0 +1,145 @@ +import { vi, describe, it, expect, beforeEach } from "vitest"; + +const { mockXeroClient } = vi.hoisted(() => ({ + mockXeroClient: { + authenticate: vi.fn(), + getRegion: vi.fn(), + tenantId: "test-tenant-id", + payrollAUV2Api: { createTimesheet: vi.fn() }, + payrollNZApi: { createTimesheet: vi.fn() }, + payrollUKApi: { createTimesheet: vi.fn() }, + }, +})); + +vi.mock("../../clients/xero-client.js", () => ({ + xeroClient: mockXeroClient, +})); + +import { createXeroPayrollTimesheet } from "../create-xero-payroll-timesheet.handler.js"; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("createXeroPayrollTimesheet", () => { + describe("AU region", () => { + beforeEach(() => { + mockXeroClient.getRegion.mockResolvedValue("AU"); + }); + + it("calls payrollAUV2Api.createTimesheet with single object", async () => { + mockXeroClient.payrollAUV2Api.createTimesheet.mockResolvedValue({ + body: { timesheet: { timesheetID: "ts-new" } }, + }); + + await createXeroPayrollTimesheet({ + employeeID: "emp-1", + startDate: "2024-01-01", + endDate: "2024-01-07", + payrollCalendarID: "cal-1", + }); + + const call = mockXeroClient.payrollAUV2Api.createTimesheet.mock.calls[0]; + expect(call[0]).toBe("test-tenant-id"); + expect(call[1].payrollCalendarID).toBe("cal-1"); + expect(call[1].employeeID).toBe("emp-1"); + }); + + it("passes timesheetLines with date and single numberOfUnits", async () => { + mockXeroClient.payrollAUV2Api.createTimesheet.mockResolvedValue({ + body: { timesheet: { timesheetID: "ts-new" } }, + }); + + await createXeroPayrollTimesheet({ + employeeID: "emp-1", + startDate: "2024-01-01", + endDate: "2024-01-07", + payrollCalendarID: "cal-1", + timesheetLines: [ + { earningsRateID: "rate-1", numberOfUnits: 8, date: "2024-01-01" }, + ], + }); + + const timesheet = mockXeroClient.payrollAUV2Api.createTimesheet.mock.calls[0][1]; + expect(timesheet.timesheetLines[0].numberOfUnits).toBe(8); + expect(timesheet.timesheetLines[0].date).toBe("2024-01-01"); + }); + }); + + describe("NZ region", () => { + beforeEach(() => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + }); + + it("calls payrollNZApi.createTimesheet with payrollCalendarID", async () => { + mockXeroClient.payrollNZApi.createTimesheet.mockResolvedValue({ + body: { timesheet: { timesheetID: "ts-new" } }, + }); + + await createXeroPayrollTimesheet({ + employeeID: "emp-1", + startDate: "2024-01-01", + endDate: "2024-01-07", + payrollCalendarID: "cal-1", + }); + + const timesheet = mockXeroClient.payrollNZApi.createTimesheet.mock.calls[0][1]; + expect(timesheet.payrollCalendarID).toBe("cal-1"); + expect(timesheet.employeeID).toBe("emp-1"); + }); + + it("maps timesheetLines with date and single numberOfUnits", async () => { + mockXeroClient.payrollNZApi.createTimesheet.mockResolvedValue({ + body: { timesheet: { timesheetID: "ts-new" } }, + }); + + await createXeroPayrollTimesheet({ + employeeID: "emp-1", + startDate: "2024-01-01", + endDate: "2024-01-07", + payrollCalendarID: "cal-1", + timesheetLines: [ + { earningsRateID: "rate-1", numberOfUnits: 8, date: "2024-01-01" }, + ], + }); + + const timesheet = mockXeroClient.payrollNZApi.createTimesheet.mock.calls[0][1]; + expect(timesheet.timesheetLines[0].date).toBe("2024-01-01"); + expect(timesheet.timesheetLines[0].numberOfUnits).toBe(8); + }); + }); + + describe("UK region", () => { + it("calls payrollUKApi.createTimesheet", async () => { + mockXeroClient.getRegion.mockResolvedValue("UK"); + mockXeroClient.payrollUKApi.createTimesheet.mockResolvedValue({ + body: { timesheet: { timesheetID: "ts-new" } }, + }); + + await createXeroPayrollTimesheet({ + employeeID: "emp-1", + startDate: "2024-01-01", + endDate: "2024-01-07", + payrollCalendarID: "cal-1", + }); + + expect(mockXeroClient.payrollUKApi.createTimesheet).toHaveBeenCalled(); + expect(mockXeroClient.payrollNZApi.createTimesheet).not.toHaveBeenCalled(); + }); + }); + + it("returns error response on API failure", async () => { + mockXeroClient.getRegion.mockResolvedValue("AU"); + mockXeroClient.payrollAUV2Api.createTimesheet.mockRejectedValue(new Error("Validation failed")); + + const result = await createXeroPayrollTimesheet({ + employeeID: "emp-1", + startDate: "2024-01-01", + endDate: "2024-01-07", + payrollCalendarID: "cal-1", + }); + + expect(result.isError).toBe(true); + expect(result.error).toBe("Validation failed"); + }); +}); diff --git a/src/handlers/__tests__/delete-xero-payroll-timesheet-line.handler.test.ts b/src/handlers/__tests__/delete-xero-payroll-timesheet-line.handler.test.ts new file mode 100644 index 00000000..ecc7cd69 --- /dev/null +++ b/src/handlers/__tests__/delete-xero-payroll-timesheet-line.handler.test.ts @@ -0,0 +1,71 @@ +import { vi, describe, it, expect, beforeEach } from "vitest"; + +const { mockXeroClient } = vi.hoisted(() => ({ + mockXeroClient: { + authenticate: vi.fn(), + getRegion: vi.fn(), + tenantId: "test-tenant-id", + payrollAUV2Api: { deleteTimesheetLine: vi.fn() }, + payrollNZApi: { deleteTimesheetLine: vi.fn() }, + payrollUKApi: { deleteTimesheetLine: vi.fn() }, + }, +})); + +vi.mock("../../clients/xero-client.js", () => ({ + xeroClient: mockXeroClient, +})); + +import { deleteXeroPayrollTimesheetLine } from "../delete-xero-payroll-timesheet-line.handler.js"; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("deleteXeroPayrollTimesheetLine", () => { + describe("AU region", () => { + it("calls payrollAUV2Api.deleteTimesheetLine", async () => { + mockXeroClient.getRegion.mockResolvedValue("AU"); + mockXeroClient.payrollAUV2Api.deleteTimesheetLine.mockResolvedValue({}); + + const result = await deleteXeroPayrollTimesheetLine("ts-1", "line-1"); + + expect(mockXeroClient.payrollAUV2Api.deleteTimesheetLine).toHaveBeenCalledWith("test-tenant-id", "ts-1", "line-1"); + expect(result.isError).toBe(false); + expect(result.result).toBe(true); + }); + }); + + describe("NZ region", () => { + it("calls payrollNZApi.deleteTimesheetLine", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.deleteTimesheetLine.mockResolvedValue({}); + + const result = await deleteXeroPayrollTimesheetLine("ts-1", "line-1"); + + expect(mockXeroClient.payrollNZApi.deleteTimesheetLine).toHaveBeenCalledWith("test-tenant-id", "ts-1", "line-1"); + expect(result.isError).toBe(false); + expect(result.result).toBe(true); + }); + }); + + describe("UK region", () => { + it("calls payrollUKApi.deleteTimesheetLine", async () => { + mockXeroClient.getRegion.mockResolvedValue("UK"); + mockXeroClient.payrollUKApi.deleteTimesheetLine.mockResolvedValue({}); + + const result = await deleteXeroPayrollTimesheetLine("ts-1", "line-1"); + + expect(mockXeroClient.payrollUKApi.deleteTimesheetLine).toHaveBeenCalledWith("test-tenant-id", "ts-1", "line-1"); + expect(mockXeroClient.payrollNZApi.deleteTimesheetLine).not.toHaveBeenCalled(); + }); + }); + + it("returns error response on API failure", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.deleteTimesheetLine.mockRejectedValue(new Error("Cannot delete line")); + + const result = await deleteXeroPayrollTimesheetLine("ts-1", "line-1"); + expect(result.isError).toBe(true); + expect(result.error).toBe("Cannot delete line"); + }); +}); diff --git a/src/handlers/__tests__/delete-xero-payroll-timesheet.handler.test.ts b/src/handlers/__tests__/delete-xero-payroll-timesheet.handler.test.ts new file mode 100644 index 00000000..488d13a3 --- /dev/null +++ b/src/handlers/__tests__/delete-xero-payroll-timesheet.handler.test.ts @@ -0,0 +1,71 @@ +import { vi, describe, it, expect, beforeEach } from "vitest"; + +const { mockXeroClient } = vi.hoisted(() => ({ + mockXeroClient: { + authenticate: vi.fn(), + getRegion: vi.fn(), + tenantId: "test-tenant-id", + payrollAUV2Api: { deleteTimesheet: vi.fn() }, + payrollNZApi: { deleteTimesheet: vi.fn() }, + payrollUKApi: { deleteTimesheet: vi.fn() }, + }, +})); + +vi.mock("../../clients/xero-client.js", () => ({ + xeroClient: mockXeroClient, +})); + +import { deleteXeroPayrollTimesheet } from "../delete-xero-payroll-timesheet.handler.js"; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("deleteXeroPayrollTimesheet", () => { + describe("AU region", () => { + it("calls payrollAUV2Api.deleteTimesheet", async () => { + mockXeroClient.getRegion.mockResolvedValue("AU"); + mockXeroClient.payrollAUV2Api.deleteTimesheet.mockResolvedValue({}); + + const result = await deleteXeroPayrollTimesheet("ts-1"); + + expect(mockXeroClient.payrollAUV2Api.deleteTimesheet).toHaveBeenCalledWith("test-tenant-id", "ts-1"); + expect(result.isError).toBe(false); + expect(result.result).toBe(true); + }); + }); + + describe("NZ region", () => { + it("calls payrollNZApi.deleteTimesheet", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.deleteTimesheet.mockResolvedValue({}); + + const result = await deleteXeroPayrollTimesheet("ts-1"); + + expect(mockXeroClient.payrollNZApi.deleteTimesheet).toHaveBeenCalledWith("test-tenant-id", "ts-1"); + expect(result.isError).toBe(false); + expect(result.result).toBe(true); + }); + }); + + describe("UK region", () => { + it("calls payrollUKApi.deleteTimesheet", async () => { + mockXeroClient.getRegion.mockResolvedValue("UK"); + mockXeroClient.payrollUKApi.deleteTimesheet.mockResolvedValue({}); + + const result = await deleteXeroPayrollTimesheet("ts-1"); + + expect(mockXeroClient.payrollUKApi.deleteTimesheet).toHaveBeenCalledWith("test-tenant-id", "ts-1"); + expect(mockXeroClient.payrollNZApi.deleteTimesheet).not.toHaveBeenCalled(); + }); + }); + + it("returns error response on API failure", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.deleteTimesheet.mockRejectedValue(new Error("Cannot delete")); + + const result = await deleteXeroPayrollTimesheet("ts-1"); + expect(result.isError).toBe(true); + expect(result.error).toBe("Cannot delete"); + }); +}); diff --git a/src/handlers/__tests__/get-xero-payroll-timesheet.handler.test.ts b/src/handlers/__tests__/get-xero-payroll-timesheet.handler.test.ts new file mode 100644 index 00000000..cfdb5cfe --- /dev/null +++ b/src/handlers/__tests__/get-xero-payroll-timesheet.handler.test.ts @@ -0,0 +1,89 @@ +import { vi, describe, it, expect, beforeEach } from "vitest"; + +const { mockXeroClient } = vi.hoisted(() => ({ + mockXeroClient: { + authenticate: vi.fn(), + getRegion: vi.fn(), + tenantId: "test-tenant-id", + payrollAUV2Api: { getTimesheet: vi.fn() }, + payrollNZApi: { getTimesheet: vi.fn() }, + payrollUKApi: { getTimesheet: vi.fn() }, + }, +})); + +vi.mock("../../clients/xero-client.js", () => ({ + xeroClient: mockXeroClient, +})); + +import { getXeroPayrollTimesheet } from "../get-xero-payroll-timesheet.handler.js"; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("getXeroPayrollTimesheet", () => { + describe("AU region", () => { + beforeEach(() => { + mockXeroClient.getRegion.mockResolvedValue("AU"); + }); + + it("calls payrollAUV2Api.getTimesheet", async () => { + mockXeroClient.payrollAUV2Api.getTimesheet.mockResolvedValue({ + body: { timesheet: { timesheetID: "ts-1", employeeID: "emp-1", startDate: "2024-01-01", endDate: "2024-01-07" } }, + }); + + const result = await getXeroPayrollTimesheet("ts-1"); + + expect(mockXeroClient.payrollAUV2Api.getTimesheet).toHaveBeenCalledWith("test-tenant-id", "ts-1"); + expect(result.isError).toBe(false); + expect(result.result?.timesheetID).toBe("ts-1"); + }); + }); + + describe("NZ region", () => { + it("calls payrollNZApi.getTimesheet", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.getTimesheet.mockResolvedValue({ + body: { timesheet: { timesheetID: "ts-1" } }, + }); + + await getXeroPayrollTimesheet("ts-1"); + + expect(mockXeroClient.payrollNZApi.getTimesheet).toHaveBeenCalledWith("test-tenant-id", "ts-1"); + }); + }); + + describe("UK region", () => { + it("calls payrollUKApi.getTimesheet", async () => { + mockXeroClient.getRegion.mockResolvedValue("UK"); + mockXeroClient.payrollUKApi.getTimesheet.mockResolvedValue({ + body: { timesheet: { timesheetID: "ts-1" } }, + }); + + await getXeroPayrollTimesheet("ts-1"); + + expect(mockXeroClient.payrollUKApi.getTimesheet).toHaveBeenCalledWith("test-tenant-id", "ts-1"); + expect(mockXeroClient.payrollNZApi.getTimesheet).not.toHaveBeenCalled(); + }); + }); + + it("returns null when timesheet not found", async () => { + mockXeroClient.getRegion.mockResolvedValue("AU"); + mockXeroClient.payrollAUV2Api.getTimesheet.mockResolvedValue({ + body: { timesheet: undefined }, + }); + + const result = await getXeroPayrollTimesheet("nonexistent"); + expect(result.isError).toBe(false); + expect(result.result).toBeNull(); + }); + + it("returns error response on API failure", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.getTimesheet.mockRejectedValue(new Error("Not found")); + + const result = await getXeroPayrollTimesheet("ts-1"); + expect(result.isError).toBe(true); + expect(result.error).toBe("Not found"); + }); +}); diff --git a/src/handlers/__tests__/list-xero-earnings-rates.handler.test.ts b/src/handlers/__tests__/list-xero-earnings-rates.handler.test.ts new file mode 100644 index 00000000..c7cd898d --- /dev/null +++ b/src/handlers/__tests__/list-xero-earnings-rates.handler.test.ts @@ -0,0 +1,113 @@ +import { vi, describe, it, expect, beforeEach } from "vitest"; + +const { mockXeroClient } = vi.hoisted(() => ({ + mockXeroClient: { + authenticate: vi.fn(), + getRegion: vi.fn(), + tenantId: "test-tenant-id", + payrollAUApi: { getPayItems: vi.fn() }, + payrollNZApi: { getEarningsRates: vi.fn() }, + payrollUKApi: { getEarningsRates: vi.fn() }, + }, +})); + +vi.mock("../../clients/xero-client.js", () => ({ + xeroClient: mockXeroClient, +})); + +import { listXeroEarningsRates } from "../list-xero-earnings-rates.handler.js"; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("listXeroEarningsRates", () => { + describe("AU region", () => { + beforeEach(() => { + mockXeroClient.getRegion.mockResolvedValue("AU"); + }); + + it("calls payrollAUApi.getPayItems and extracts earningsRates", async () => { + mockXeroClient.payrollAUApi.getPayItems.mockResolvedValue({ + body: { + payItems: { + earningsRates: [ + { + earningsRateID: "rate-1", + name: "Regular Hours", + earningsType: "ORDINARYTIMEEARNINGS", + rateType: "RATEPERUNIT", + ratePerUnit: 25.0, + }, + { + earningsRateID: "rate-2", + name: "Overtime", + earningsType: "OVERTIMEEARNINGS", + rateType: "RATEPERUNIT", + ratePerUnit: 37.5, + }, + ], + deductionTypes: [], + leaveTypes: [], + }, + }, + }); + + const result = await listXeroEarningsRates({}); + + expect(mockXeroClient.payrollAUApi.getPayItems).toHaveBeenCalled(); + expect(result.isError).toBe(false); + expect(result.result).toHaveLength(2); + expect(result.result![0].name).toBe("Regular Hours"); + expect(result.result![1].name).toBe("Overtime"); + }); + }); + + describe("NZ region", () => { + it("calls payrollNZApi.getEarningsRates", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.getEarningsRates.mockResolvedValue({ + body: { + earningsRates: [ + { + earningsRateID: "rate-1", + name: "Ordinary Time", + earningsType: "RegularEarnings", + rateType: "RatePerUnit", + ratePerUnit: 30.0, + }, + ], + }, + }); + + const result = await listXeroEarningsRates({}); + + expect(mockXeroClient.payrollNZApi.getEarningsRates).toHaveBeenCalledWith("test-tenant-id", undefined); + expect(result.isError).toBe(false); + expect(result.result).toHaveLength(1); + }); + }); + + describe("UK region", () => { + it("calls payrollUKApi.getEarningsRates", async () => { + mockXeroClient.getRegion.mockResolvedValue("UK"); + mockXeroClient.payrollUKApi.getEarningsRates.mockResolvedValue({ + body: { earningsRates: [] }, + }); + + await listXeroEarningsRates({}); + + expect(mockXeroClient.payrollUKApi.getEarningsRates).toHaveBeenCalled(); + expect(mockXeroClient.payrollNZApi.getEarningsRates).not.toHaveBeenCalled(); + }); + }); + + it("returns error response on API failure", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.getEarningsRates.mockRejectedValue(new Error("API error")); + + const result = await listXeroEarningsRates({}); + expect(result.isError).toBe(true); + expect(result.error).toBe("API error"); + }); +}); diff --git a/src/handlers/__tests__/list-xero-payroll-calendars.handler.test.ts b/src/handlers/__tests__/list-xero-payroll-calendars.handler.test.ts new file mode 100644 index 00000000..03fda15d --- /dev/null +++ b/src/handlers/__tests__/list-xero-payroll-calendars.handler.test.ts @@ -0,0 +1,110 @@ +import { vi, describe, it, expect, beforeEach } from "vitest"; + +const { mockXeroClient } = vi.hoisted(() => ({ + mockXeroClient: { + authenticate: vi.fn(), + getRegion: vi.fn(), + tenantId: "test-tenant-id", + payrollAUApi: { getPayrollCalendars: vi.fn() }, + payrollNZApi: { getPayRunCalendars: vi.fn() }, + payrollUKApi: { getPayRunCalendars: vi.fn() }, + }, +})); + +vi.mock("../../clients/xero-client.js", () => ({ + xeroClient: mockXeroClient, +})); + +import { listXeroPayrollCalendars } from "../list-xero-payroll-calendars.handler.js"; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("listXeroPayrollCalendars", () => { + describe("AU region", () => { + beforeEach(() => { + mockXeroClient.getRegion.mockResolvedValue("AU"); + }); + + it("calls payrollAUApi.getPayrollCalendars and maps fields", async () => { + mockXeroClient.payrollAUApi.getPayrollCalendars.mockResolvedValue({ + body: { + payrollCalendars: [ + { + payrollCalendarID: "cal-1", + name: "Weekly", + calendarType: "WEEKLY", + startDate: "2024-01-15", + paymentDate: "2024-01-22", + }, + ], + }, + }); + + const result = await listXeroPayrollCalendars({}); + + expect(mockXeroClient.payrollAUApi.getPayrollCalendars).toHaveBeenCalledWith( + "test-tenant-id", + undefined, + undefined, + undefined, + undefined, + ); + expect(result.isError).toBe(false); + expect(result.result).toHaveLength(1); + expect(result.result![0].periodStartDate).toBe("2024-01-15"); + expect(result.result![0].paymentDate).toBe("2024-01-22"); + }); + }); + + describe("NZ region", () => { + it("calls payrollNZApi.getPayRunCalendars", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.getPayRunCalendars.mockResolvedValue({ + body: { + payRunCalendars: [ + { + payrollCalendarID: "cal-1", + name: "Monthly", + calendarType: "MONTHLY", + periodStartDate: "2024-01-01", + periodEndDate: "2024-01-31", + paymentDate: "2024-02-01", + }, + ], + }, + }); + + const result = await listXeroPayrollCalendars({}); + + expect(mockXeroClient.payrollNZApi.getPayRunCalendars).toHaveBeenCalledWith("test-tenant-id", undefined); + expect(result.isError).toBe(false); + expect(result.result![0].periodStartDate).toBe("2024-01-01"); + expect(result.result![0].periodEndDate).toBe("2024-01-31"); + }); + }); + + describe("UK region", () => { + it("calls payrollUKApi.getPayRunCalendars", async () => { + mockXeroClient.getRegion.mockResolvedValue("UK"); + mockXeroClient.payrollUKApi.getPayRunCalendars.mockResolvedValue({ + body: { payRunCalendars: [] }, + }); + + await listXeroPayrollCalendars({}); + + expect(mockXeroClient.payrollUKApi.getPayRunCalendars).toHaveBeenCalled(); + expect(mockXeroClient.payrollNZApi.getPayRunCalendars).not.toHaveBeenCalled(); + }); + }); + + it("returns error response on API failure", async () => { + mockXeroClient.getRegion.mockResolvedValue("AU"); + mockXeroClient.payrollAUApi.getPayrollCalendars.mockRejectedValue(new Error("Unauthorized")); + + const result = await listXeroPayrollCalendars({}); + expect(result.isError).toBe(true); + expect(result.error).toBe("Unauthorized"); + }); +}); diff --git a/src/handlers/__tests__/list-xero-timesheets.handler.test.ts b/src/handlers/__tests__/list-xero-timesheets.handler.test.ts new file mode 100644 index 00000000..3db0fae0 --- /dev/null +++ b/src/handlers/__tests__/list-xero-timesheets.handler.test.ts @@ -0,0 +1,138 @@ +import { vi, describe, it, expect, beforeEach } from "vitest"; + +const { mockXeroClient } = vi.hoisted(() => ({ + mockXeroClient: { + authenticate: vi.fn(), + getRegion: vi.fn(), + tenantId: "test-tenant-id", + payrollAUV2Api: { getTimesheets: vi.fn() }, + payrollNZApi: { getTimesheets: vi.fn() }, + payrollUKApi: { getTimesheets: vi.fn() }, + }, +})); + +vi.mock("../../clients/xero-client.js", () => ({ + xeroClient: mockXeroClient, +})); + +import { listXeroPayrollTimesheets } from "../list-xero-timesheets.handler.js"; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("listXeroPayrollTimesheets", () => { + describe("AU region", () => { + beforeEach(() => { + mockXeroClient.getRegion.mockResolvedValue("AU"); + }); + + it("calls payrollAUV2Api.getTimesheets with v2 params", async () => { + mockXeroClient.payrollAUV2Api.getTimesheets.mockResolvedValue({ + body: { timesheets: [] }, + }); + + await listXeroPayrollTimesheets({ + page: 1, + filter: "employeeId==abc-123", + status: "Draft", + startDate: "2024-01-01", + endDate: "2024-01-31", + sort: "startDate", + }); + + expect(mockXeroClient.payrollAUV2Api.getTimesheets).toHaveBeenCalledWith( + "test-tenant-id", + 1, + "employeeId==abc-123", + "Draft", + "2024-01-01", + "2024-01-31", + "startDate", + ); + }); + + it("returns AU timesheets on success", async () => { + const auTimesheets = [ + { timesheetID: "ts-1", employeeID: "emp-1", startDate: "2024-01-01", endDate: "2024-01-07", totalHours: 40 }, + ]; + mockXeroClient.payrollAUV2Api.getTimesheets.mockResolvedValue({ + body: { timesheets: auTimesheets }, + }); + + const result = await listXeroPayrollTimesheets({}); + expect(result.isError).toBe(false); + expect(result.result).toEqual(auTimesheets); + }); + }); + + describe("NZ region", () => { + beforeEach(() => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + }); + + it("calls payrollNZApi.getTimesheets with correct params", async () => { + mockXeroClient.payrollNZApi.getTimesheets.mockResolvedValue({ + body: { timesheets: [] }, + }); + + await listXeroPayrollTimesheets({ + page: 1, + filter: "employeeId==abc-123", + status: "Draft", + startDate: "2024-01-01", + endDate: "2024-01-31", + sort: "startDate", + }); + + expect(mockXeroClient.payrollNZApi.getTimesheets).toHaveBeenCalledWith( + "test-tenant-id", + 1, + "employeeId==abc-123", + "Draft", + "2024-01-01", + "2024-01-31", + "startDate", + ); + }); + + it("returns NZ timesheets on success", async () => { + const nzTimesheets = [ + { timesheetID: "ts-1", employeeID: "emp-1", startDate: "2024-01-01", endDate: "2024-01-07", totalHours: 40 }, + ]; + mockXeroClient.payrollNZApi.getTimesheets.mockResolvedValue({ + body: { timesheets: nzTimesheets }, + }); + + const result = await listXeroPayrollTimesheets({}); + expect(result.isError).toBe(false); + expect(result.result).toEqual(nzTimesheets); + }); + }); + + describe("UK region", () => { + it("calls payrollUKApi.getTimesheets", async () => { + mockXeroClient.getRegion.mockResolvedValue("UK"); + mockXeroClient.payrollUKApi.getTimesheets.mockResolvedValue({ + body: { timesheets: [] }, + }); + + await listXeroPayrollTimesheets({ page: 1 }); + + expect(mockXeroClient.payrollUKApi.getTimesheets).toHaveBeenCalled(); + expect(mockXeroClient.payrollNZApi.getTimesheets).not.toHaveBeenCalled(); + }); + }); + + describe("error handling", () => { + it("returns error response on API failure", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.getTimesheets.mockRejectedValue(new Error("API down")); + + const result = await listXeroPayrollTimesheets({}); + expect(result.isError).toBe(true); + expect(result.error).toBe("API down"); + expect(result.result).toBeNull(); + }); + }); +}); diff --git a/src/handlers/__tests__/revert-xero-payroll-timesheet.handler.test.ts b/src/handlers/__tests__/revert-xero-payroll-timesheet.handler.test.ts new file mode 100644 index 00000000..c5023b73 --- /dev/null +++ b/src/handlers/__tests__/revert-xero-payroll-timesheet.handler.test.ts @@ -0,0 +1,78 @@ +import { vi, describe, it, expect, beforeEach } from "vitest"; + +const { mockXeroClient } = vi.hoisted(() => ({ + mockXeroClient: { + authenticate: vi.fn(), + getRegion: vi.fn(), + tenantId: "test-tenant-id", + payrollAUV2Api: { revertTimesheet: vi.fn() }, + payrollNZApi: { revertTimesheet: vi.fn() }, + payrollUKApi: { revertTimesheet: vi.fn() }, + }, +})); + +vi.mock("../../clients/xero-client.js", () => ({ + xeroClient: mockXeroClient, +})); + +import { revertXeroPayrollTimesheet } from "../revert-xero-payroll-timesheet.handler.js"; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("revertXeroPayrollTimesheet", () => { + describe("AU region", () => { + beforeEach(() => { + mockXeroClient.getRegion.mockResolvedValue("AU"); + }); + + it("calls payrollAUV2Api.revertTimesheet", async () => { + mockXeroClient.payrollAUV2Api.revertTimesheet.mockResolvedValue({ + body: { timesheet: { timesheetID: "ts-1", status: "Draft" } }, + }); + + const result = await revertXeroPayrollTimesheet("ts-1"); + + expect(mockXeroClient.payrollAUV2Api.revertTimesheet).toHaveBeenCalledWith("test-tenant-id", "ts-1"); + expect(result.isError).toBe(false); + expect(result.result?.timesheetID).toBe("ts-1"); + }); + }); + + describe("NZ region", () => { + it("calls payrollNZApi.revertTimesheet", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.revertTimesheet.mockResolvedValue({ + body: { timesheet: { timesheetID: "ts-1" } }, + }); + + await revertXeroPayrollTimesheet("ts-1"); + + expect(mockXeroClient.payrollNZApi.revertTimesheet).toHaveBeenCalledWith("test-tenant-id", "ts-1"); + }); + }); + + describe("UK region", () => { + it("calls payrollUKApi.revertTimesheet", async () => { + mockXeroClient.getRegion.mockResolvedValue("UK"); + mockXeroClient.payrollUKApi.revertTimesheet.mockResolvedValue({ + body: { timesheet: { timesheetID: "ts-1" } }, + }); + + await revertXeroPayrollTimesheet("ts-1"); + + expect(mockXeroClient.payrollUKApi.revertTimesheet).toHaveBeenCalledWith("test-tenant-id", "ts-1"); + expect(mockXeroClient.payrollNZApi.revertTimesheet).not.toHaveBeenCalled(); + }); + }); + + it("returns error response on API failure", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.revertTimesheet.mockRejectedValue(new Error("Cannot revert")); + + const result = await revertXeroPayrollTimesheet("ts-1"); + expect(result.isError).toBe(true); + expect(result.error).toBe("Cannot revert"); + }); +}); diff --git a/src/handlers/__tests__/update-timesheet-line.handler.test.ts b/src/handlers/__tests__/update-timesheet-line.handler.test.ts new file mode 100644 index 00000000..1d9cb776 --- /dev/null +++ b/src/handlers/__tests__/update-timesheet-line.handler.test.ts @@ -0,0 +1,123 @@ +import { vi, describe, it, expect, beforeEach } from "vitest"; + +const { mockXeroClient } = vi.hoisted(() => ({ + mockXeroClient: { + authenticate: vi.fn(), + getRegion: vi.fn(), + tenantId: "test-tenant-id", + payrollAUV2Api: { updateTimesheetLine: vi.fn() }, + payrollNZApi: { updateTimesheetLine: vi.fn() }, + payrollUKApi: { updateTimesheetLine: vi.fn() }, + }, +})); + +vi.mock("../../clients/xero-client.js", () => ({ + xeroClient: mockXeroClient, +})); + +import { updateXeroPayrollTimesheetUpdateLine } from "../update-xero-payroll-timesheet-update-line.handler.js"; + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("updateXeroPayrollTimesheetUpdateLine", () => { + describe("AU region", () => { + beforeEach(() => { + mockXeroClient.getRegion.mockResolvedValue("AU"); + }); + + it("calls payrollAUV2Api.updateTimesheetLine with timesheetLineID", async () => { + mockXeroClient.payrollAUV2Api.updateTimesheetLine.mockResolvedValue({ + body: { timesheetLine: { timesheetLineID: "line-1", earningsRateID: "rate-1" } }, + }); + + const result = await updateXeroPayrollTimesheetUpdateLine({ + timesheetID: "ts-1", + timesheetLineID: "line-1", + earningsRateID: "rate-1", + numberOfUnits: 6, + date: "2024-01-01", + }); + + expect(mockXeroClient.payrollAUV2Api.updateTimesheetLine).toHaveBeenCalledWith( + "test-tenant-id", + "ts-1", + "line-1", + expect.objectContaining({ + earningsRateID: "rate-1", + numberOfUnits: 6, + date: "2024-01-01", + }), + ); + expect(result.isError).toBe(false); + }); + }); + + describe("NZ region", () => { + beforeEach(() => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + }); + + it("calls payrollNZApi.updateTimesheetLine with timesheetLineID", async () => { + mockXeroClient.payrollNZApi.updateTimesheetLine.mockResolvedValue({ + body: { timesheetLine: { timesheetLineID: "line-1" } }, + }); + + await updateXeroPayrollTimesheetUpdateLine({ + timesheetID: "ts-1", + timesheetLineID: "line-1", + earningsRateID: "rate-1", + numberOfUnits: 8, + date: "2024-01-01", + }); + + expect(mockXeroClient.payrollNZApi.updateTimesheetLine).toHaveBeenCalledWith( + "test-tenant-id", + "ts-1", + "line-1", + expect.objectContaining({ + earningsRateID: "rate-1", + numberOfUnits: 8, + date: "2024-01-01", + }), + ); + }); + }); + + describe("UK region", () => { + it("calls payrollUKApi.updateTimesheetLine", async () => { + mockXeroClient.getRegion.mockResolvedValue("UK"); + mockXeroClient.payrollUKApi.updateTimesheetLine.mockResolvedValue({ + body: { timesheetLine: { timesheetLineID: "line-1" } }, + }); + + await updateXeroPayrollTimesheetUpdateLine({ + timesheetID: "ts-1", + timesheetLineID: "line-1", + earningsRateID: "rate-1", + numberOfUnits: 8, + date: "2024-01-01", + }); + + expect(mockXeroClient.payrollUKApi.updateTimesheetLine).toHaveBeenCalled(); + expect(mockXeroClient.payrollNZApi.updateTimesheetLine).not.toHaveBeenCalled(); + }); + }); + + it("returns error response on API failure", async () => { + mockXeroClient.getRegion.mockResolvedValue("NZ"); + mockXeroClient.payrollNZApi.updateTimesheetLine.mockRejectedValue(new Error("Line not found")); + + const result = await updateXeroPayrollTimesheetUpdateLine({ + timesheetID: "ts-1", + timesheetLineID: "line-1", + earningsRateID: "rate-1", + numberOfUnits: 8, + date: "2024-01-01", + }); + + expect(result.isError).toBe(true); + expect(result.error).toBe("Line not found"); + }); +}); diff --git a/src/handlers/approve-xero-payroll-timesheet.handler.ts b/src/handlers/approve-xero-payroll-timesheet.handler.ts index 5d07eeff..9f3e0cc9 100644 --- a/src/handlers/approve-xero-payroll-timesheet.handler.ts +++ b/src/handlers/approve-xero-payroll-timesheet.handler.ts @@ -1,40 +1,32 @@ -import { Timesheet } from "xero-node/dist/gen/model/payroll-nz/timesheet.js"; +import { NzTimesheet } from "../types/payroll-nz-types.js"; +import { AuV2Timesheet } from "../types/payroll-au-v2-types.js"; import { xeroClient } from "../clients/xero-client.js"; import { formatError } from "../helpers/format-error.js"; import { XeroClientResponse } from "../types/tool-response.js"; -async function approveTimesheet(timesheetID: string): Promise { - await xeroClient.authenticate(); +export async function approveXeroPayrollTimesheet( + timesheetID: string, +): Promise> { + try { + await xeroClient.authenticate(); + const region = await xeroClient.getRegion(); - // Call the approveTimesheet endpoint from the PayrollNZApi - const approvedTimesheet = await xeroClient.payrollNZApi.approveTimesheet( - xeroClient.tenantId, - timesheetID, - ); + let result: NzTimesheet | AuV2Timesheet | null; - return approvedTimesheet.body.timesheet ?? null; -} + if (region === "AU") { + const res = await xeroClient.payrollAUV2Api.approveTimesheet(xeroClient.tenantId, timesheetID); + result = res.body.timesheet ?? null; + } else if (region === "UK") { + const res = await xeroClient.payrollUKApi.approveTimesheet(xeroClient.tenantId, timesheetID); + result = (res.body.timesheet ?? null) as unknown as NzTimesheet | null; + } else { + const res = await xeroClient.payrollNZApi.approveTimesheet(xeroClient.tenantId, timesheetID); + result = res.body.timesheet ?? null; + } -/** - * Approve a payroll timesheet in Xero - */ -export async function approveXeroPayrollTimesheet(timesheetID: string): Promise< - XeroClientResponse -> { - try { - const approvedTimesheet = await approveTimesheet(timesheetID); - - return { - result: approvedTimesheet, - isError: false, - error: null, - }; + return { result, isError: false, error: null }; } catch (error) { - return { - result: null, - isError: true, - error: formatError(error), - }; + return { result: null, isError: true, error: formatError(error) }; } -} \ No newline at end of file +} diff --git a/src/handlers/create-xero-payroll-timesheet.handler.ts b/src/handlers/create-xero-payroll-timesheet.handler.ts index d5dcd7d2..8c5f58c7 100644 --- a/src/handlers/create-xero-payroll-timesheet.handler.ts +++ b/src/handlers/create-xero-payroll-timesheet.handler.ts @@ -1,40 +1,59 @@ -import { Timesheet } from "xero-node/dist/gen/model/payroll-nz/timesheet.js"; +import { NzTimesheet } from "../types/payroll-nz-types.js"; +import { AuV2Timesheet } from "../types/payroll-au-v2-types.js"; import { xeroClient } from "../clients/xero-client.js"; import { formatError } from "../helpers/format-error.js"; import { XeroClientResponse } from "../types/tool-response.js"; -async function createTimesheet(timesheet: Timesheet): Promise { - await xeroClient.authenticate(); - - // Call the createTimesheet endpoint from the PayrollNZApi - const createdTimesheet = await xeroClient.payrollNZApi.createTimesheet( - xeroClient.tenantId, - timesheet, - ); - - return createdTimesheet.body.timesheet ?? null; +export interface CreateTimesheetParams { + employeeID: string; + startDate: string; + endDate: string; + payrollCalendarID: string; + timesheetLines?: Array<{ + earningsRateID: string; + numberOfUnits: number; + date: string; + trackingItemID?: string; + }>; } -/** - * Create a payroll timesheet in Xero - */ -export async function createXeroPayrollTimesheet(timesheet: Timesheet): Promise< - XeroClientResponse -> { +export async function createXeroPayrollTimesheet( + params: CreateTimesheetParams, +): Promise> { try { - const newTimesheet = await createTimesheet(timesheet); + await xeroClient.authenticate(); + const region = await xeroClient.getRegion(); - return { - result: newTimesheet, - isError: false, - error: null, + const timesheet = { + payrollCalendarID: params.payrollCalendarID, + employeeID: params.employeeID, + startDate: params.startDate, + endDate: params.endDate, + timesheetLines: params.timesheetLines?.map((line) => ({ + earningsRateID: line.earningsRateID, + numberOfUnits: line.numberOfUnits, + date: line.date, + trackingItemID: line.trackingItemID, + })), }; + + let result: NzTimesheet | AuV2Timesheet | null; + + if (region === "AU") { + const res = await xeroClient.payrollAUV2Api.createTimesheet(xeroClient.tenantId, timesheet as AuV2Timesheet); + result = res.body.timesheet ?? null; + } else if (region === "UK") { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const res = await xeroClient.payrollUKApi.createTimesheet(xeroClient.tenantId, timesheet as any); + result = (res.body.timesheet ?? null) as unknown as NzTimesheet | null; + } else { + const res = await xeroClient.payrollNZApi.createTimesheet(xeroClient.tenantId, timesheet as NzTimesheet); + result = res.body.timesheet ?? null; + } + + return { result, isError: false, error: null }; } catch (error) { - return { - result: null, - isError: true, - error: formatError(error), - }; + return { result: null, isError: true, error: formatError(error) }; } -} \ No newline at end of file +} diff --git a/src/handlers/delete-xero-payroll-timesheet-line.handler.ts b/src/handlers/delete-xero-payroll-timesheet-line.handler.ts new file mode 100644 index 00000000..45464818 --- /dev/null +++ b/src/handlers/delete-xero-payroll-timesheet-line.handler.ts @@ -0,0 +1,25 @@ +import { xeroClient } from "../clients/xero-client.js"; +import { formatError } from "../helpers/format-error.js"; +import { XeroClientResponse } from "../types/tool-response.js"; + +export async function deleteXeroPayrollTimesheetLine( + timesheetID: string, + timesheetLineID: string, +): Promise> { + try { + await xeroClient.authenticate(); + const region = await xeroClient.getRegion(); + + if (region === "AU") { + await xeroClient.payrollAUV2Api.deleteTimesheetLine(xeroClient.tenantId, timesheetID, timesheetLineID); + } else if (region === "UK") { + await xeroClient.payrollUKApi.deleteTimesheetLine(xeroClient.tenantId, timesheetID, timesheetLineID); + } else { + await xeroClient.payrollNZApi.deleteTimesheetLine(xeroClient.tenantId, timesheetID, timesheetLineID); + } + + return { result: true, isError: false, error: null }; + } catch (error) { + return { result: null, isError: true, error: formatError(error) }; + } +} diff --git a/src/handlers/delete-xero-payroll-timesheet.handler.ts b/src/handlers/delete-xero-payroll-timesheet.handler.ts index 0b90058a..09f13124 100644 --- a/src/handlers/delete-xero-payroll-timesheet.handler.ts +++ b/src/handlers/delete-xero-payroll-timesheet.handler.ts @@ -2,34 +2,23 @@ import { xeroClient } from "../clients/xero-client.js"; import { formatError } from "../helpers/format-error.js"; import { XeroClientResponse } from "../types/tool-response.js"; -async function deleteTimesheet(timesheetID: string): Promise { - await xeroClient.authenticate(); - - // Call the deleteTimesheet endpoint from the PayrollNZApi - await xeroClient.payrollNZApi.deleteTimesheet(xeroClient.tenantId, timesheetID); - - return true; -} - -/** - * Delete an existing payroll timesheet in Xero - */ -export async function deleteXeroPayrollTimesheet(timesheetID: string): Promise< - XeroClientResponse -> { +export async function deleteXeroPayrollTimesheet( + timesheetID: string, +): Promise> { try { - await deleteTimesheet(timesheetID); + await xeroClient.authenticate(); + const region = await xeroClient.getRegion(); + + if (region === "AU") { + await xeroClient.payrollAUV2Api.deleteTimesheet(xeroClient.tenantId, timesheetID); + } else if (region === "UK") { + await xeroClient.payrollUKApi.deleteTimesheet(xeroClient.tenantId, timesheetID); + } else { + await xeroClient.payrollNZApi.deleteTimesheet(xeroClient.tenantId, timesheetID); + } - return { - result: true, - isError: false, - error: null, - }; + return { result: true, isError: false, error: null }; } catch (error) { - return { - result: null, - isError: true, - error: formatError(error), - }; + return { result: null, isError: true, error: formatError(error) }; } -} \ No newline at end of file +} diff --git a/src/handlers/get-xero-payroll-timesheet.handler.ts b/src/handlers/get-xero-payroll-timesheet.handler.ts index 66e46ee3..b32d10b9 100644 --- a/src/handlers/get-xero-payroll-timesheet.handler.ts +++ b/src/handlers/get-xero-payroll-timesheet.handler.ts @@ -1,40 +1,32 @@ -import { Timesheet } from "xero-node/dist/gen/model/payroll-nz/timesheet.js"; +import { NzTimesheet } from "../types/payroll-nz-types.js"; +import { AuV2Timesheet } from "../types/payroll-au-v2-types.js"; import { xeroClient } from "../clients/xero-client.js"; import { formatError } from "../helpers/format-error.js"; import { XeroClientResponse } from "../types/tool-response.js"; -async function getTimesheet(timesheetID: string): Promise { - await xeroClient.authenticate(); +export async function getXeroPayrollTimesheet( + timesheetID: string, +): Promise> { + try { + await xeroClient.authenticate(); + const region = await xeroClient.getRegion(); - // Call the Timesheet endpoint from the PayrollNZApi - const timesheet = await xeroClient.payrollNZApi.getTimesheet( - xeroClient.tenantId, - timesheetID, - ); + let result: NzTimesheet | AuV2Timesheet | null; - return timesheet.body.timesheet ?? null; -} + if (region === "AU") { + const res = await xeroClient.payrollAUV2Api.getTimesheet(xeroClient.tenantId, timesheetID); + result = res.body.timesheet ?? null; + } else if (region === "UK") { + const res = await xeroClient.payrollUKApi.getTimesheet(xeroClient.tenantId, timesheetID); + result = (res.body.timesheet ?? null) as unknown as NzTimesheet | null; + } else { + const res = await xeroClient.payrollNZApi.getTimesheet(xeroClient.tenantId, timesheetID); + result = res.body.timesheet ?? null; + } -/** - * Get a single payroll timesheet from Xero - */ -export async function getXeroPayrollTimesheet(timesheetID: string): Promise< - XeroClientResponse -> { - try { - const timesheet = await getTimesheet(timesheetID); - - return { - result: timesheet, - isError: false, - error: null, - }; + return { result, isError: false, error: null }; } catch (error) { - return { - result: null, - isError: true, - error: formatError(error), - }; + return { result: null, isError: true, error: formatError(error) }; } -} \ No newline at end of file +} diff --git a/src/handlers/list-xero-earnings-rates.handler.ts b/src/handlers/list-xero-earnings-rates.handler.ts new file mode 100644 index 00000000..7cc7ed31 --- /dev/null +++ b/src/handlers/list-xero-earnings-rates.handler.ts @@ -0,0 +1,68 @@ +import { NzEarningsRate } from "../types/payroll-nz-types.js"; +import { AuEarningsRate } from "../types/payroll-au-types.js"; + +import { xeroClient } from "../clients/xero-client.js"; +import { formatError } from "../helpers/format-error.js"; +import { XeroClientResponse } from "../types/tool-response.js"; + +export interface EarningsRateResult { + earningsRateID: string; + name: string; + earningsType: string; + rateType: string; + ratePerUnit?: number; + fixedAmount?: number; + currentRecord?: boolean; +} + +export interface ListEarningsRatesParams { + page?: number; +} + +export async function listXeroEarningsRates( + params: ListEarningsRatesParams = {}, +): Promise> { + try { + await xeroClient.authenticate(); + const region = await xeroClient.getRegion(); + + let rates: EarningsRateResult[]; + + if (region === "AU") { + const res = await xeroClient.payrollAUApi.getPayItems( + xeroClient.tenantId, + undefined, + undefined, + undefined, + params.page, + ); + + const earningsRates = res.body.payItems?.earningsRates ?? []; + rates = earningsRates.map((r: AuEarningsRate) => ({ + earningsRateID: r.earningsRateID ?? "", + name: r.name ?? "", + earningsType: String(r.earningsType ?? ""), + rateType: String(r.rateType ?? ""), + ratePerUnit: r.ratePerUnit ? Number(r.ratePerUnit) : undefined, + currentRecord: r.currentRecord, + })); + } else { + const api = region === "UK" ? xeroClient.payrollUKApi : xeroClient.payrollNZApi; + const res = await api.getEarningsRates(xeroClient.tenantId, params.page); + + rates = ((res.body.earningsRates ?? []) as NzEarningsRate[]).map((r) => ({ + earningsRateID: r.earningsRateID ?? "", + name: r.name ?? "", + earningsType: String(r.earningsType ?? ""), + rateType: String(r.rateType ?? ""), + ratePerUnit: r.ratePerUnit, + fixedAmount: r.fixedAmount, + currentRecord: r.currentRecord, + })); + } + + return { result: rates, isError: false, error: null }; + } catch (error) { + return { result: null, isError: true, error: formatError(error) }; + } +} diff --git a/src/handlers/list-xero-payroll-calendars.handler.ts b/src/handlers/list-xero-payroll-calendars.handler.ts new file mode 100644 index 00000000..a5d97a16 --- /dev/null +++ b/src/handlers/list-xero-payroll-calendars.handler.ts @@ -0,0 +1,64 @@ +import { PayRunCalendar } from "../types/payroll-nz-types.js"; +import { AuPayrollCalendar } from "../types/payroll-au-types.js"; + +import { xeroClient } from "../clients/xero-client.js"; +import { formatError } from "../helpers/format-error.js"; +import { XeroClientResponse } from "../types/tool-response.js"; + +export interface PayrollCalendarResult { + payrollCalendarID: string; + name: string; + calendarType: string; + periodStartDate: string; + periodEndDate?: string; + paymentDate: string; +} + +export interface ListPayrollCalendarsParams { + page?: number; +} + +export async function listXeroPayrollCalendars( + params: ListPayrollCalendarsParams = {}, +): Promise> { + try { + await xeroClient.authenticate(); + const region = await xeroClient.getRegion(); + + let calendars: PayrollCalendarResult[]; + + if (region === "AU") { + const res = await xeroClient.payrollAUApi.getPayrollCalendars( + xeroClient.tenantId, + undefined, + undefined, + undefined, + params.page, + ); + + calendars = (res.body.payrollCalendars ?? []).map((c: AuPayrollCalendar) => ({ + payrollCalendarID: c.payrollCalendarID ?? "", + name: c.name ?? "", + calendarType: String(c.calendarType ?? ""), + periodStartDate: c.startDate ?? "", + paymentDate: c.paymentDate ?? "", + })); + } else { + const api = region === "UK" ? xeroClient.payrollUKApi : xeroClient.payrollNZApi; + const res = await api.getPayRunCalendars(xeroClient.tenantId, params.page); + + calendars = ((res.body.payRunCalendars ?? []) as PayRunCalendar[]).map((c) => ({ + payrollCalendarID: c.payrollCalendarID ?? "", + name: c.name ?? "", + calendarType: String(c.calendarType ?? ""), + periodStartDate: c.periodStartDate ?? "", + periodEndDate: c.periodEndDate, + paymentDate: c.paymentDate ?? "", + })); + } + + return { result: calendars, isError: false, error: null }; + } catch (error) { + return { result: null, isError: true, error: formatError(error) }; + } +} diff --git a/src/handlers/list-xero-timesheets.handler.ts b/src/handlers/list-xero-timesheets.handler.ts index 39d198a6..a99b3648 100644 --- a/src/handlers/list-xero-timesheets.handler.ts +++ b/src/handlers/list-xero-timesheets.handler.ts @@ -1,41 +1,51 @@ -import { Timesheet } from "xero-node/dist/gen/model/payroll-nz/timesheet.js"; +import { NzTimesheet } from "../types/payroll-nz-types.js"; +import { AuV2Timesheet } from "../types/payroll-au-v2-types.js"; import { xeroClient } from "../clients/xero-client.js"; import { formatError } from "../helpers/format-error.js"; import { XeroClientResponse } from "../types/tool-response.js"; -async function getTimesheets(): Promise { - await xeroClient.authenticate(); - - // Call the Timesheets endpoint from the PayrollNZApi - const timesheets = await xeroClient.payrollNZApi.getTimesheets( - xeroClient.tenantId, - undefined, // page - undefined, // filter - ); - - return timesheets.body.timesheets ?? []; +export interface ListTimesheetsParams { + page?: number; + filter?: string; + status?: string; + startDate?: string; + endDate?: string; + sort?: string; } -/** - * List all payroll timesheets from Xero - */ -export async function listXeroPayrollTimesheets(): Promise< - XeroClientResponse -> { +export async function listXeroPayrollTimesheets( + params: ListTimesheetsParams = {}, +): Promise> { try { - const timesheets = await getTimesheets(); + await xeroClient.authenticate(); + const region = await xeroClient.getRegion(); + + const args = [ + xeroClient.tenantId, + params.page, + params.filter, + params.status, + params.startDate, + params.endDate, + params.sort, + ] as const; - return { - result: timesheets, - isError: false, - error: null, - }; + let timesheets: (NzTimesheet | AuV2Timesheet)[]; + + if (region === "AU") { + const res = await xeroClient.payrollAUV2Api.getTimesheets(...args); + timesheets = res.body.timesheets ?? []; + } else if (region === "UK") { + const res = await xeroClient.payrollUKApi.getTimesheets(...args); + timesheets = (res.body.timesheets ?? []) as unknown as NzTimesheet[]; + } else { + const res = await xeroClient.payrollNZApi.getTimesheets(...args); + timesheets = res.body.timesheets ?? []; + } + + return { result: timesheets, isError: false, error: null }; } catch (error) { - return { - result: null, - isError: true, - error: formatError(error), - }; + return { result: null, isError: true, error: formatError(error) }; } -} \ No newline at end of file +} diff --git a/src/handlers/revert-xero-payroll-timesheet.handler.ts b/src/handlers/revert-xero-payroll-timesheet.handler.ts index db61214f..bf14fadf 100644 --- a/src/handlers/revert-xero-payroll-timesheet.handler.ts +++ b/src/handlers/revert-xero-payroll-timesheet.handler.ts @@ -1,40 +1,32 @@ -import { Timesheet } from "xero-node/dist/gen/model/payroll-nz/timesheet.js"; +import { NzTimesheet } from "../types/payroll-nz-types.js"; +import { AuV2Timesheet } from "../types/payroll-au-v2-types.js"; import { xeroClient } from "../clients/xero-client.js"; import { formatError } from "../helpers/format-error.js"; import { XeroClientResponse } from "../types/tool-response.js"; -async function revertTimesheet(timesheetID: string): Promise { - await xeroClient.authenticate(); +export async function revertXeroPayrollTimesheet( + timesheetID: string, +): Promise> { + try { + await xeroClient.authenticate(); + const region = await xeroClient.getRegion(); - // Call the revertTimesheet endpoint from the PayrollNZApi - const revertedTimesheet = await xeroClient.payrollNZApi.revertTimesheet( - xeroClient.tenantId, - timesheetID, - ); + let result: NzTimesheet | AuV2Timesheet | null; - return revertedTimesheet.body.timesheet ?? null; -} + if (region === "AU") { + const res = await xeroClient.payrollAUV2Api.revertTimesheet(xeroClient.tenantId, timesheetID); + result = res.body.timesheet ?? null; + } else if (region === "UK") { + const res = await xeroClient.payrollUKApi.revertTimesheet(xeroClient.tenantId, timesheetID); + result = (res.body.timesheet ?? null) as unknown as NzTimesheet | null; + } else { + const res = await xeroClient.payrollNZApi.revertTimesheet(xeroClient.tenantId, timesheetID); + result = res.body.timesheet ?? null; + } -/** - * Revert a payroll timesheet to draft in Xero - */ -export async function revertXeroPayrollTimesheet(timesheetID: string): Promise< - XeroClientResponse -> { - try { - const revertedTimesheet = await revertTimesheet(timesheetID); - - return { - result: revertedTimesheet, - isError: false, - error: null, - }; + return { result, isError: false, error: null }; } catch (error) { - return { - result: null, - isError: true, - error: formatError(error), - }; + return { result: null, isError: true, error: formatError(error) }; } -} \ No newline at end of file +} diff --git a/src/handlers/update-xero-payroll-timesheet-add-line.handler.ts b/src/handlers/update-xero-payroll-timesheet-add-line.handler.ts index 78a3e6e6..c4affcd7 100644 --- a/src/handlers/update-xero-payroll-timesheet-add-line.handler.ts +++ b/src/handlers/update-xero-payroll-timesheet-add-line.handler.ts @@ -1,43 +1,48 @@ -import { - TimesheetLine, -} from "xero-node/dist/gen/model/payroll-nz/timesheetLine.js"; +import { NzTimesheetLine } from "../types/payroll-nz-types.js"; +import { AuV2TimesheetLine } from "../types/payroll-au-v2-types.js"; import { xeroClient } from "../clients/xero-client.js"; import { formatError } from "../helpers/format-error.js"; import { XeroClientResponse } from "../types/tool-response.js"; -async function addTimesheetLine(timesheetID: string, timesheetLine: TimesheetLine): Promise { - await xeroClient.authenticate(); - - // Call the createTimesheetLine endpoint from the PayrollNZApi - const createdLine = await xeroClient.payrollNZApi.createTimesheetLine( - xeroClient.tenantId, - timesheetID, - timesheetLine, - ); - - return createdLine.body.timesheetLine ?? null; +export interface AddTimesheetLineParams { + timesheetID: string; + earningsRateID: string; + numberOfUnits: number; + date: string; + trackingItemID?: string; } -/** - * Add a timesheet line to an existing payroll timesheet in Xero - */ -export async function updateXeroPayrollTimesheetAddLine(timesheetID: string, timesheetLine: TimesheetLine): Promise< - XeroClientResponse -> { +export async function updateXeroPayrollTimesheetAddLine( + params: AddTimesheetLineParams, +): Promise> { try { - const newLine = await addTimesheetLine(timesheetID, timesheetLine); + await xeroClient.authenticate(); + const region = await xeroClient.getRegion(); - return { - result: newLine, - isError: false, - error: null, + const line = { + earningsRateID: params.earningsRateID, + numberOfUnits: params.numberOfUnits, + date: params.date, + trackingItemID: params.trackingItemID, }; + + let result: NzTimesheetLine | AuV2TimesheetLine | null; + + if (region === "AU") { + const res = await xeroClient.payrollAUV2Api.createTimesheetLine(xeroClient.tenantId, params.timesheetID, line as AuV2TimesheetLine); + result = res.body.timesheetLine ?? null; + } else if (region === "UK") { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const res = await xeroClient.payrollUKApi.createTimesheetLine(xeroClient.tenantId, params.timesheetID, line as any); + result = (res.body.timesheetLine ?? null) as unknown as NzTimesheetLine | null; + } else { + const res = await xeroClient.payrollNZApi.createTimesheetLine(xeroClient.tenantId, params.timesheetID, line as NzTimesheetLine); + result = res.body.timesheetLine ?? null; + } + + return { result, isError: false, error: null }; } catch (error) { - return { - result: null, - isError: true, - error: formatError(error), - }; + return { result: null, isError: true, error: formatError(error) }; } -} \ No newline at end of file +} diff --git a/src/handlers/update-xero-payroll-timesheet-update-line.handler.ts b/src/handlers/update-xero-payroll-timesheet-update-line.handler.ts index 23b575eb..17f37d0b 100644 --- a/src/handlers/update-xero-payroll-timesheet-update-line.handler.ts +++ b/src/handlers/update-xero-payroll-timesheet-update-line.handler.ts @@ -1,50 +1,49 @@ -import { - TimesheetLine, -} from "xero-node/dist/gen/model/payroll-nz/timesheetLine.js"; +import { NzTimesheetLine } from "../types/payroll-nz-types.js"; +import { AuV2TimesheetLine } from "../types/payroll-au-v2-types.js"; import { xeroClient } from "../clients/xero-client.js"; import { formatError } from "../helpers/format-error.js"; import { XeroClientResponse } from "../types/tool-response.js"; -async function updateTimesheetLine( - timesheetID: string, - timesheetLineID: string, - timesheetLine: TimesheetLine -): Promise { - await xeroClient.authenticate(); - - // Call the updateTimesheetLine endpoint from the PayrollNZApi - const updatedLine = await xeroClient.payrollNZApi.updateTimesheetLine( - xeroClient.tenantId, - timesheetID, - timesheetLineID, - timesheetLine, - ); - - return updatedLine.body.timesheetLine ?? null; +export interface UpdateTimesheetLineParams { + timesheetID: string; + timesheetLineID: string; + earningsRateID: string; + numberOfUnits: number; + date: string; + trackingItemID?: string; } -/** - * Update an existing timesheet line in a payroll timesheet in Xero - */ export async function updateXeroPayrollTimesheetUpdateLine( - timesheetID: string, - timesheetLineID: string, - timesheetLine: TimesheetLine -): Promise> { + params: UpdateTimesheetLineParams, +): Promise> { try { - const updatedLine = await updateTimesheetLine(timesheetID, timesheetLineID, timesheetLine); + await xeroClient.authenticate(); + const region = await xeroClient.getRegion(); - return { - result: updatedLine, - isError: false, - error: null, + const line = { + earningsRateID: params.earningsRateID, + numberOfUnits: params.numberOfUnits, + date: params.date, + trackingItemID: params.trackingItemID, }; + + let result: NzTimesheetLine | AuV2TimesheetLine | null; + + if (region === "AU") { + const res = await xeroClient.payrollAUV2Api.updateTimesheetLine(xeroClient.tenantId, params.timesheetID, params.timesheetLineID, line as AuV2TimesheetLine); + result = res.body.timesheetLine ?? null; + } else if (region === "UK") { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const res = await xeroClient.payrollUKApi.updateTimesheetLine(xeroClient.tenantId, params.timesheetID, params.timesheetLineID, line as any); + result = (res.body.timesheetLine ?? null) as unknown as NzTimesheetLine | null; + } else { + const res = await xeroClient.payrollNZApi.updateTimesheetLine(xeroClient.tenantId, params.timesheetID, params.timesheetLineID, line as NzTimesheetLine); + result = res.body.timesheetLine ?? null; + } + + return { result, isError: false, error: null }; } catch (error) { - return { - result: null, - isError: true, - error: formatError(error), - }; + return { result: null, isError: true, error: formatError(error) }; } -} \ No newline at end of file +} diff --git a/src/tools/create/create-payroll-timesheet.tool.ts b/src/tools/create/create-payroll-timesheet.tool.ts index 59ec3f98..f4b6ad88 100644 --- a/src/tools/create/create-payroll-timesheet.tool.ts +++ b/src/tools/create/create-payroll-timesheet.tool.ts @@ -1,15 +1,15 @@ -import { Timesheet } from "xero-node/dist/gen/model/payroll-nz/timesheet.js"; import { z } from "zod"; import { createXeroPayrollTimesheet, + CreateTimesheetParams, } from "../../handlers/create-xero-payroll-timesheet.handler.js"; import { CreateXeroTool } from "../../helpers/create-xero-tool.js"; const CreatePayrollTimesheetTool = CreateXeroTool( "create-timesheet", `Create a new payroll timesheet in Xero. -This allows you to specify details such as the employee ID, payroll calendar ID, start and end dates, and timesheet lines.`, +Supports AU, NZ, and UK payroll regions. The organisation's region is auto-detected.`, { payrollCalendarID: z.string().describe("The ID of the payroll calendar."), employeeID: z.string().describe("The ID of the employee."), @@ -19,14 +19,15 @@ This allows you to specify details such as the employee ID, payroll calendar ID, .array( z.object({ earningsRateID: z.string().describe("The ID of the earnings rate."), - numberOfUnits: z.number().describe("The number of units for the timesheet line."), + numberOfUnits: z.number().describe("The number of units (hours) for this line."), date: z.string().describe("The date for the timesheet line (YYYY-MM-DD)."), + trackingItemID: z.string().optional().describe("Optional tracking category item ID."), }) ) .optional() .describe("The lines of the timesheet."), }, - async (params: Timesheet) => { + async (params: CreateTimesheetParams) => { const response = await createXeroPayrollTimesheet(params); if (response.isError) { @@ -53,4 +54,4 @@ This allows you to specify details such as the employee ID, payroll calendar ID, }, ); -export default CreatePayrollTimesheetTool; \ No newline at end of file +export default CreatePayrollTimesheetTool; diff --git a/src/tools/delete/delete-payroll-timesheet-line.tool.ts b/src/tools/delete/delete-payroll-timesheet-line.tool.ts new file mode 100644 index 00000000..62f4614d --- /dev/null +++ b/src/tools/delete/delete-payroll-timesheet-line.tool.ts @@ -0,0 +1,41 @@ +import { z } from "zod"; + +import { + deleteXeroPayrollTimesheetLine, +} from "../../handlers/delete-xero-payroll-timesheet-line.handler.js"; +import { CreateXeroTool } from "../../helpers/create-xero-tool.js"; + +const DeletePayrollTimesheetLineTool = CreateXeroTool( + "delete-timesheet-line", + `Delete a specific line from a payroll timesheet in Xero. +Supports AU, NZ, and UK payroll regions. The organisation's region is auto-detected.`, + { + timesheetID: z.string().describe("The ID of the timesheet containing the line."), + timesheetLineID: z.string().describe("The ID of the timesheet line to delete."), + }, + async (params: { timesheetID: string; timesheetLineID: string }) => { + const response = await deleteXeroPayrollTimesheetLine(params.timesheetID, params.timesheetLineID); + + if (response.isError) { + return { + content: [ + { + type: "text" as const, + text: `Error deleting timesheet line: ${response.error}`, + }, + ], + }; + } + + return { + content: [ + { + type: "text" as const, + text: `Successfully deleted timesheet line with ID: ${params.timesheetLineID}`, + }, + ], + }; + }, +); + +export default DeletePayrollTimesheetLineTool; diff --git a/src/tools/delete/delete-payroll-timesheet.tool.ts b/src/tools/delete/delete-payroll-timesheet.tool.ts index 8529a29c..c4935e3b 100644 --- a/src/tools/delete/delete-payroll-timesheet.tool.ts +++ b/src/tools/delete/delete-payroll-timesheet.tool.ts @@ -7,7 +7,8 @@ import { CreateXeroTool } from "../../helpers/create-xero-tool.js"; const DeletePayrollTimesheetTool = CreateXeroTool( "delete-timesheet", - `Delete an existing payroll timesheet in Xero by its ID.`, + `Delete an existing payroll timesheet in Xero by its ID. +Supports AU, NZ, and UK payroll regions. The organisation's region is auto-detected.`, { timesheetID: z.string().describe("The ID of the timesheet to delete."), }, diff --git a/src/tools/delete/index.ts b/src/tools/delete/index.ts index b3d11429..7425b980 100644 --- a/src/tools/delete/index.ts +++ b/src/tools/delete/index.ts @@ -1,5 +1,7 @@ import DeletePayrollTimesheetTool from "./delete-payroll-timesheet.tool.js"; +import DeletePayrollTimesheetLineTool from "./delete-payroll-timesheet-line.tool.js"; export const DeleteTools = [ - DeletePayrollTimesheetTool + DeletePayrollTimesheetTool, + DeletePayrollTimesheetLineTool, ]; diff --git a/src/tools/get/get-payroll-timesheet.tool.ts b/src/tools/get/get-payroll-timesheet.tool.ts index 034c29c3..1bf4b7c2 100644 --- a/src/tools/get/get-payroll-timesheet.tool.ts +++ b/src/tools/get/get-payroll-timesheet.tool.ts @@ -8,7 +8,8 @@ import { CreateXeroTool } from "../../helpers/create-xero-tool.js"; const GetPayrollTimesheetTool = CreateXeroTool( "get-timesheet", `Retrieve a single payroll timesheet from Xero by its ID. -This provides details such as the timesheet ID, employee ID, start and end dates, total hours, and the last updated date.`, +This provides details such as the timesheet ID, employee ID, start and end dates, total hours, and the last updated date. +Supports AU, NZ, and UK payroll regions. The organisation's region is auto-detected.`, { timesheetID: z.string().describe("The ID of the timesheet to retrieve."), }, @@ -40,20 +41,14 @@ This provides details such as the timesheet ID, employee ID, start and end dates }; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const ts = timesheet as any; + return { content: [ { type: "text" as const, - text: [ - `Timesheet ID: ${timesheet.timesheetID}`, - `Employee ID: ${timesheet.employeeID}`, - `Start Date: ${timesheet.startDate}`, - `End Date: ${timesheet.endDate}`, - `Total Hours: ${timesheet.totalHours}`, - `Last Updated: ${timesheet.updatedDateUTC}`, - ] - .filter(Boolean) - .join("\n"), + text: JSON.stringify(ts, null, 2), }, ], }; diff --git a/src/tools/list/index.ts b/src/tools/list/index.ts index b4d1b627..b89f4bd2 100644 --- a/src/tools/list/index.ts +++ b/src/tools/list/index.ts @@ -28,6 +28,8 @@ import ListTaxRatesTool from "./list-tax-rates.tool.js"; import ListTrackingCategoriesTool from "./list-tracking-categories.tool.js"; import ListTrialBalanceTool from "./list-trial-balance.tool.js"; import ListContactGroupsTool from "./list-contact-groups.tool.js"; +import ListPayrollCalendarsTool from "./list-payroll-calendars.tool.js"; +import ListEarningsRatesTool from "./list-earnings-rates.tool.js"; export const ListTools = [ ListAccountsTool, @@ -54,5 +56,7 @@ export const ListTools = [ ListAgedPayablesByContact, ListPayrollTimesheetsTool, ListContactGroupsTool, - ListTrackingCategoriesTool + ListTrackingCategoriesTool, + ListPayrollCalendarsTool, + ListEarningsRatesTool, ]; diff --git a/src/tools/list/list-earnings-rates.tool.ts b/src/tools/list/list-earnings-rates.tool.ts new file mode 100644 index 00000000..4be0353a --- /dev/null +++ b/src/tools/list/list-earnings-rates.tool.ts @@ -0,0 +1,60 @@ +import { z } from "zod"; + +import { + listXeroEarningsRates, + ListEarningsRatesParams, + EarningsRateResult, +} from "../../handlers/list-xero-earnings-rates.handler.js"; +import { CreateXeroTool } from "../../helpers/create-xero-tool.js"; + +const ListEarningsRatesTool = CreateXeroTool( + "list-earnings-rates", + `List all earnings rates in Xero Payroll. +This shows the earnings rate IDs, names, types, and rates needed when creating timesheet lines. +For AU: retrieves earnings rates from Pay Items. For NZ/UK: retrieves from the dedicated earnings rates endpoint. +Supports AU, NZ, and UK payroll regions. The organisation's region is auto-detected.`, + { + page: z.number().optional().describe("Page number for pagination."), + }, + async (params: ListEarningsRatesParams) => { + const response = await listXeroEarningsRates(params); + + if (response.isError) { + return { + content: [ + { + type: "text" as const, + text: `Error listing earnings rates: ${response.error}`, + }, + ], + }; + } + + const rates = response.result; + + return { + content: [ + { + type: "text" as const, + text: `Found ${rates?.length || 0} earnings rates:`, + }, + ...(rates?.map((rate: EarningsRateResult) => ({ + type: "text" as const, + text: [ + `Name: ${rate.name}`, + `Earnings Rate ID: ${rate.earningsRateID}`, + `Earnings Type: ${rate.earningsType}`, + `Rate Type: ${rate.rateType}`, + rate.ratePerUnit !== undefined ? `Rate Per Unit: ${rate.ratePerUnit}` : null, + rate.fixedAmount !== undefined ? `Fixed Amount: ${rate.fixedAmount}` : null, + rate.currentRecord !== undefined ? `Active: ${rate.currentRecord ? "Yes" : "No"}` : null, + ] + .filter(Boolean) + .join("\n"), + })) || []), + ], + }; + }, +); + +export default ListEarningsRatesTool; diff --git a/src/tools/list/list-payroll-calendars.tool.ts b/src/tools/list/list-payroll-calendars.tool.ts new file mode 100644 index 00000000..0194bfb0 --- /dev/null +++ b/src/tools/list/list-payroll-calendars.tool.ts @@ -0,0 +1,58 @@ +import { z } from "zod"; + +import { + listXeroPayrollCalendars, + ListPayrollCalendarsParams, + PayrollCalendarResult, +} from "../../handlers/list-xero-payroll-calendars.handler.js"; +import { CreateXeroTool } from "../../helpers/create-xero-tool.js"; + +const ListPayrollCalendarsTool = CreateXeroTool( + "list-payroll-calendars", + `List all payroll calendars in Xero. +This shows pay calendar names, types, upcoming period start dates, and payment dates. +Supports AU, NZ, and UK payroll regions. The organisation's region is auto-detected.`, + { + page: z.number().optional().describe("Page number for pagination."), + }, + async (params: ListPayrollCalendarsParams) => { + const response = await listXeroPayrollCalendars(params); + + if (response.isError) { + return { + content: [ + { + type: "text" as const, + text: `Error listing payroll calendars: ${response.error}`, + }, + ], + }; + } + + const calendars = response.result; + + return { + content: [ + { + type: "text" as const, + text: `Found ${calendars?.length || 0} payroll calendars:`, + }, + ...(calendars?.map((cal: PayrollCalendarResult) => ({ + type: "text" as const, + text: [ + `Calendar: ${cal.name}`, + `Calendar ID: ${cal.payrollCalendarID}`, + `Type: ${cal.calendarType}`, + `Next Period Start: ${cal.periodStartDate}`, + cal.periodEndDate ? `Period End: ${cal.periodEndDate}` : null, + `Payment Date: ${cal.paymentDate}`, + ] + .filter(Boolean) + .join("\n"), + })) || []), + ], + }; + }, +); + +export default ListPayrollCalendarsTool; diff --git a/src/tools/list/list-payroll-timesheets.tool.ts b/src/tools/list/list-payroll-timesheets.tool.ts index 5ffd7695..0393517f 100644 --- a/src/tools/list/list-payroll-timesheets.tool.ts +++ b/src/tools/list/list-payroll-timesheets.tool.ts @@ -1,17 +1,26 @@ -import { Timesheet } from "xero-node/dist/gen/model/payroll-nz/timesheet.js"; +import { z } from "zod"; import { listXeroPayrollTimesheets, + ListTimesheetsParams, } from "../../handlers/list-xero-timesheets.handler.js"; import { CreateXeroTool } from "../../helpers/create-xero-tool.js"; const ListPayrollTimesheetsTool = CreateXeroTool( "list-timesheets", `List all payroll timesheets in Xero. -This retrieves comprehensive timesheet details including timesheet IDs, employee IDs, start and end dates, total hours, and the last updated date.`, - {}, - async () => { - const response = await listXeroPayrollTimesheets(); +This retrieves comprehensive timesheet details including timesheet IDs, employee IDs, start and end dates, total hours, and the last updated date. +Supports AU, NZ, and UK payroll regions. The organisation's region is auto-detected.`, + { + page: z.number().optional().describe("Page number for pagination."), + filter: z.string().optional().describe("Filter by employeeId and/or payrollCalendarId."), + status: z.string().optional().describe("Filter by timesheet status (Draft, Approved, Completed)."), + startDate: z.string().optional().describe("Return timesheets with startDate on or after this date (YYYY-MM-DD)."), + endDate: z.string().optional().describe("Return timesheets with endDate on or before this date (YYYY-MM-DD)."), + sort: z.string().optional().describe("Sort order: createdDate (default) or startDate."), + }, + async (params: ListTimesheetsParams) => { + const response = await listXeroPayrollTimesheets(params); if (response.isError) { return { @@ -32,14 +41,16 @@ This retrieves comprehensive timesheet details including timesheet IDs, employee type: "text" as const, text: `Found ${timesheets?.length || 0} timesheets:`, }, - ...(timesheets?.map((timesheet: Timesheet) => ({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...(timesheets?.map((timesheet: any) => ({ type: "text" as const, text: [ `Timesheet ID: ${timesheet.timesheetID}`, `Employee ID: ${timesheet.employeeID}`, `Start Date: ${timesheet.startDate}`, `End Date: ${timesheet.endDate}`, - `Total Hours: ${timesheet.totalHours}`, + `Status: ${timesheet.status}`, + `Total Hours: ${timesheet.totalHours ?? "N/A"}`, `Last Updated: ${timesheet.updatedDateUTC}`, ] .filter(Boolean) @@ -50,4 +61,4 @@ This retrieves comprehensive timesheet details including timesheet IDs, employee }, ); -export default ListPayrollTimesheetsTool; \ No newline at end of file +export default ListPayrollTimesheetsTool; diff --git a/src/tools/update/approve-payroll-timesheet.tool.ts b/src/tools/update/approve-payroll-timesheet.tool.ts index a9daf330..9a9ffbbd 100644 --- a/src/tools/update/approve-payroll-timesheet.tool.ts +++ b/src/tools/update/approve-payroll-timesheet.tool.ts @@ -7,7 +7,8 @@ import { CreateXeroTool } from "../../helpers/create-xero-tool.js"; const ApprovePayrollTimesheetTool = CreateXeroTool( "approve-timesheet", - `Approve a payroll timesheet in Xero by its ID.`, + `Approve a payroll timesheet in Xero by its ID. +Supports AU, NZ, and UK payroll regions. The organisation's region is auto-detected.`, { timesheetID: z.string().describe("The ID of the timesheet to approve."), }, diff --git a/src/tools/update/revert-payroll-timesheet.tool.ts b/src/tools/update/revert-payroll-timesheet.tool.ts index 17ac5f4a..adc94179 100644 --- a/src/tools/update/revert-payroll-timesheet.tool.ts +++ b/src/tools/update/revert-payroll-timesheet.tool.ts @@ -7,7 +7,8 @@ import { CreateXeroTool } from "../../helpers/create-xero-tool.js"; const RevertPayrollTimesheetTool = CreateXeroTool( "revert-timesheet", - `Revert a payroll timesheet to draft in Xero by its ID.`, + `Revert a payroll timesheet to draft in Xero by its ID. +Supports AU, NZ, and UK payroll regions. The organisation's region is auto-detected.`, { timesheetID: z.string().describe("The ID of the timesheet to revert."), }, diff --git a/src/tools/update/update-payroll-timesheet-add-line.tool.ts b/src/tools/update/update-payroll-timesheet-add-line.tool.ts index 498efecf..7a5b2dc1 100644 --- a/src/tools/update/update-payroll-timesheet-add-line.tool.ts +++ b/src/tools/update/update-payroll-timesheet-add-line.tool.ts @@ -1,27 +1,34 @@ -import { - TimesheetLine, -} from "xero-node/dist/gen/model/payroll-nz/timesheetLine.js"; import { z } from "zod"; import { updateXeroPayrollTimesheetAddLine, + AddTimesheetLineParams, } from "../../handlers/update-xero-payroll-timesheet-add-line.handler.js"; import { CreateXeroTool } from "../../helpers/create-xero-tool.js"; const AddTimesheetLineTool = CreateXeroTool( "add-timesheet-line", - `Add a new timesheet line to an existing payroll timesheet in Xero.`, + `Add a new timesheet line to an existing payroll timesheet in Xero. +Supports AU, NZ, and UK payroll regions. The organisation's region is auto-detected.`, { timesheetID: z.string().describe("The ID of the timesheet to update."), timesheetLine: z.object({ earningsRateID: z.string().describe("The ID of the earnings rate."), - numberOfUnits: z.number().describe("The number of units for the timesheet line."), + numberOfUnits: z.number().describe("The number of units (hours) for this line."), date: z.string().describe("The date for the timesheet line (YYYY-MM-DD)."), + trackingItemID: z.string().optional().describe("Optional tracking category item ID."), }).describe("The details of the timesheet line to add."), }, - async (params: { timesheetID: string; timesheetLine: TimesheetLine }) => { - const { timesheetID, timesheetLine } = params; - const response = await updateXeroPayrollTimesheetAddLine(timesheetID, timesheetLine); + async (params: { timesheetID: string; timesheetLine: { earningsRateID: string; numberOfUnits: number; date: string; trackingItemID?: string } }) => { + const addParams: AddTimesheetLineParams = { + timesheetID: params.timesheetID, + earningsRateID: params.timesheetLine.earningsRateID, + numberOfUnits: params.timesheetLine.numberOfUnits, + date: params.timesheetLine.date, + trackingItemID: params.timesheetLine.trackingItemID, + }; + + const response = await updateXeroPayrollTimesheetAddLine(addParams); if (response.isError) { return { @@ -34,17 +41,15 @@ const AddTimesheetLineTool = CreateXeroTool( }; } - const newLine = response.result; - return { content: [ { type: "text" as const, - text: `Successfully added timesheet line with date: ${newLine?.date}`, + text: `Successfully added timesheet line for earnings rate: ${params.timesheetLine.earningsRateID}`, }, ], }; }, ); -export default AddTimesheetLineTool; \ No newline at end of file +export default AddTimesheetLineTool; diff --git a/src/tools/update/update-payroll-timesheet-update-line.tool.ts b/src/tools/update/update-payroll-timesheet-update-line.tool.ts index 58afbb0c..23999a59 100644 --- a/src/tools/update/update-payroll-timesheet-update-line.tool.ts +++ b/src/tools/update/update-payroll-timesheet-update-line.tool.ts @@ -1,28 +1,36 @@ -import { - TimesheetLine, -} from "xero-node/dist/gen/model/payroll-nz/timesheetLine.js"; import { z } from "zod"; import { updateXeroPayrollTimesheetUpdateLine, + UpdateTimesheetLineParams, } from "../../handlers/update-xero-payroll-timesheet-update-line.handler.js"; import { CreateXeroTool } from "../../helpers/create-xero-tool.js"; const UpdatePayrollTimesheetLineTool = CreateXeroTool( "update-timesheet-line", - `Update an existing timesheet line in a payroll timesheet in Xero.`, + `Update an existing timesheet line in a payroll timesheet in Xero. +Supports AU, NZ, and UK payroll regions. The organisation's region is auto-detected.`, { timesheetID: z.string().describe("The ID of the timesheet to update."), timesheetLineID: z.string().describe("The ID of the timesheet line to update."), timesheetLine: z.object({ earningsRateID: z.string().describe("The ID of the earnings rate."), - numberOfUnits: z.number().describe("The number of units for the timesheet line."), + numberOfUnits: z.number().describe("The number of units (hours) for this line."), date: z.string().describe("The date for the timesheet line (YYYY-MM-DD)."), + trackingItemID: z.string().optional().describe("Optional tracking category item ID."), }).describe("The details of the timesheet line to update."), }, - async (params: { timesheetID: string; timesheetLineID: string; timesheetLine: TimesheetLine }) => { - const { timesheetID, timesheetLineID, timesheetLine } = params; - const response = await updateXeroPayrollTimesheetUpdateLine(timesheetID, timesheetLineID, timesheetLine); + async (params: { timesheetID: string; timesheetLineID: string; timesheetLine: { earningsRateID: string; numberOfUnits: number; date: string; trackingItemID?: string } }) => { + const updateParams: UpdateTimesheetLineParams = { + timesheetID: params.timesheetID, + timesheetLineID: params.timesheetLineID, + earningsRateID: params.timesheetLine.earningsRateID, + numberOfUnits: params.timesheetLine.numberOfUnits, + date: params.timesheetLine.date, + trackingItemID: params.timesheetLine.trackingItemID, + }; + + const response = await updateXeroPayrollTimesheetUpdateLine(updateParams); if (response.isError) { return { @@ -35,17 +43,15 @@ const UpdatePayrollTimesheetLineTool = CreateXeroTool( }; } - const updatedLine = response.result; - return { content: [ { type: "text" as const, - text: `Successfully updated timesheet line with date: ${updatedLine?.date}`, + text: `Successfully updated timesheet line for earnings rate: ${params.timesheetLine.earningsRateID}`, }, ], }; }, ); -export default UpdatePayrollTimesheetLineTool; \ No newline at end of file +export default UpdatePayrollTimesheetLineTool; diff --git a/src/types/payroll-au-types.ts b/src/types/payroll-au-types.ts index a9613b06..5007216c 100644 --- a/src/types/payroll-au-types.ts +++ b/src/types/payroll-au-types.ts @@ -1,3 +1,6 @@ export { Timesheet as AuTimesheet } from "xero-node/dist/gen/model/payroll-au/timesheet.js"; export { TimesheetLine as AuTimesheetLine } from "xero-node/dist/gen/model/payroll-au/timesheetLine.js"; export { TimesheetStatus } from "xero-node/dist/gen/model/payroll-au/timesheetStatus.js"; +export { PayrollCalendar as AuPayrollCalendar } from "xero-node/dist/gen/model/payroll-au/payrollCalendar.js"; +export { PayItem as AuPayItem } from "xero-node/dist/gen/model/payroll-au/payItem.js"; +export { EarningsRate as AuEarningsRate } from "xero-node/dist/gen/model/payroll-au/earningsRate.js"; diff --git a/src/types/payroll-au-v2-types.ts b/src/types/payroll-au-v2-types.ts new file mode 100644 index 00000000..b166e645 --- /dev/null +++ b/src/types/payroll-au-v2-types.ts @@ -0,0 +1,2 @@ +export { Timesheet as AuV2Timesheet } from "xero-node/dist/gen/model/payroll-au-v2/timesheet.js"; +export { TimesheetLine as AuV2TimesheetLine } from "xero-node/dist/gen/model/payroll-au-v2/timesheetLine.js"; diff --git a/src/types/payroll-nz-types.ts b/src/types/payroll-nz-types.ts index 8bcdf093..df78a48f 100644 --- a/src/types/payroll-nz-types.ts +++ b/src/types/payroll-nz-types.ts @@ -6,3 +6,5 @@ export { EmployeeLeaveBalance } from "xero-node/dist/gen/model/payroll-nz/employ export { EmployeeLeaveType } from "xero-node/dist/gen/model/payroll-nz/employeeLeaveType.js"; export { LeavePeriod } from "xero-node/dist/gen/model/payroll-nz/leavePeriod.js"; export { LeaveType } from "xero-node/dist/gen/model/payroll-nz/leaveType.js"; +export { PayRunCalendar } from "xero-node/dist/gen/model/payroll-nz/payRunCalendar.js"; +export { EarningsRate as NzEarningsRate } from "xero-node/dist/gen/model/payroll-nz/earningsRate.js";