diff --git a/package-lock.json b/package-lock.json index 1d92f4ef6..3dd856e60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,8 @@ "@peculiar/x509": "^1.12.3", "adm-zip": "^0.5.16", "chalk": "^5.3.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "dot-object": "^2.1.5", "dotenv": "^16.4.5", "enquirer": "^2.4.1", @@ -231,6 +233,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", "engines": { "node": ">=0.1.90" } @@ -252,6 +255,7 @@ "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "aix" @@ -267,6 +271,7 @@ "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -282,6 +287,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -297,6 +303,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -312,6 +319,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -327,6 +335,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -342,6 +351,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -357,6 +367,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -372,6 +383,7 @@ "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -387,6 +399,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -402,6 +415,7 @@ "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -417,6 +431,7 @@ "cpu": [ "loong64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -432,6 +447,7 @@ "cpu": [ "mips64el" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -447,6 +463,7 @@ "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -462,6 +479,7 @@ "cpu": [ "riscv64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -477,6 +495,7 @@ "cpu": [ "s390x" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -492,6 +511,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -507,6 +527,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -522,6 +543,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -537,6 +559,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -552,6 +575,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "sunos" @@ -567,6 +591,7 @@ "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -582,6 +607,7 @@ "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -597,6 +623,7 @@ "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -637,6 +664,7 @@ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz", "integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", @@ -651,6 +679,7 @@ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.0.tgz", "integrity": "sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -659,6 +688,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -677,6 +707,21 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -688,11 +733,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/@eslint/js": { "version": "9.15.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz", "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -702,6 +753,7 @@ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -711,6 +763,7 @@ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "levn": "^0.4.1" }, @@ -1105,13 +1158,13 @@ } }, "node_modules/@grpc/proto-loader": { - "version": "0.7.13", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", - "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", + "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", - "protobufjs": "^7.2.5", + "protobufjs": "^7.2.4", "yargs": "^17.7.2" }, "bin": { @@ -1179,6 +1232,7 @@ "version": "2.54.0-beta.1", "resolved": "https://registry.npmjs.org/@hashgraph/sdk/-/sdk-2.54.0-beta.1.tgz", "integrity": "sha512-rXbwp24fr04LJcOtISabwtKtb0qiRHGa3ltY1KALJ0fEYlUogSEf8DmCeOzNWiZEyy9boCequlRajAkJ+1OF3A==", + "license": "Apache-2.0", "dependencies": { "@ethersproject/abi": "^5.7.0", "@ethersproject/bignumber": "^5.7.0", @@ -1264,6 +1318,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -1276,6 +1331,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.0.2.tgz", "integrity": "sha512-+gznPl8ip8P8HYHYecDtUtdsh1t2jvb+sWCD72GAiZ9m45RqwrLmReDaqdC0umQfamtFXVRoMVJ2/qINKGm9Tg==", + "license": "MIT", "dependencies": { "@inquirer/core": "^10.1.0", "@inquirer/figures": "^1.0.8", @@ -1294,6 +1350,7 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.0.2.tgz", "integrity": "sha512-KJLUHOaKnNCYzwVbryj3TNBxyZIrr56fR5N45v6K9IPrbT6B7DcudBMfylkV1A8PUdJE15mybkEQyp2/ZUpxUA==", + "license": "MIT", "dependencies": { "@inquirer/core": "^10.1.0", "@inquirer/type": "^3.0.1" @@ -1309,6 +1366,7 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.0.tgz", "integrity": "sha512-I+ETk2AL+yAVbvuKx5AJpQmoaWhpiTFOg/UJb7ZkMAK4blmtG8ATh5ct+T/8xNld0CZG/2UhtkdMwpgvld92XQ==", + "license": "MIT", "dependencies": { "@inquirer/figures": "^1.0.8", "@inquirer/type": "^3.0.1", @@ -1328,6 +1386,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", "engines": { "node": ">=14" }, @@ -1339,6 +1398,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.1.0.tgz", "integrity": "sha512-K1gGWsxEqO23tVdp5MT3H799OZ4ER1za7Dlc8F4um0W7lwSv0KGR/YyrUEyimj0g7dXZd8XknM/5QA2/Uy+TbA==", + "license": "MIT", "dependencies": { "@inquirer/core": "^10.1.0", "@inquirer/type": "^3.0.1", @@ -1355,6 +1415,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.2.tgz", "integrity": "sha512-WdgCX1cUtinz+syKyZdJomovULYlKUWZbVYZzhf+ZeeYf4htAQ3jLymoNs3koIAKfZZl3HUBb819ClCBfyznaw==", + "license": "MIT", "dependencies": { "@inquirer/core": "^10.1.0", "@inquirer/type": "^3.0.1", @@ -1371,6 +1432,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.8.tgz", "integrity": "sha512-tKd+jsmhq21AP1LhexC0pPwsCxEhGgAkg28byjJAd+xhmIs8LUX8JbUc3vBf3PhLxWiB5EvyBE5X7JSPAqMAqg==", + "license": "MIT", "engines": { "node": ">=18" } @@ -1379,6 +1441,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.0.2.tgz", "integrity": "sha512-yCLCraigU085EcdpIVEDgyfGv4vBiE4I+k1qRkc9C5dMjWF42ADMGy1RFU94+eZlz4YlkmFsiyHZy0W1wdhaNg==", + "license": "MIT", "dependencies": { "@inquirer/core": "^10.1.0", "@inquirer/type": "^3.0.1" @@ -1394,6 +1457,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.2.tgz", "integrity": "sha512-MKQhYofdUNk7eqJtz52KvM1dH6R93OMrqHduXCvuefKrsiMjHiMwjc3NZw5Imm2nqY7gWd9xdhYrtcHMJQZUxA==", + "license": "MIT", "dependencies": { "@inquirer/core": "^10.1.0", "@inquirer/type": "^3.0.1" @@ -1409,6 +1473,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.2.tgz", "integrity": "sha512-tQXGSu7IO07gsYlGy3VgXRVsbOWqFBMbqAUrJSc1PDTQQ5Qdm+QVwkP0OC0jnUZ62D19iPgXOMO+tnWG+HhjNQ==", + "license": "MIT", "dependencies": { "@inquirer/core": "^10.1.0", "@inquirer/type": "^3.0.1", @@ -1425,6 +1490,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.1.0.tgz", "integrity": "sha512-5U/XiVRH2pp1X6gpNAjWOglMf38/Ys522ncEHIKT1voRUvSj/DQnR22OVxHnwu5S+rCFaUiPQ57JOtMFQayqYA==", + "license": "MIT", "dependencies": { "@inquirer/checkbox": "^4.0.2", "@inquirer/confirm": "^5.0.2", @@ -1448,6 +1514,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.2.tgz", "integrity": "sha512-3XGcskMoVF8H0Dl1S5TSZ3rMPPBWXRcM0VeNVsS4ByWeWjSeb0lPqfnBg6N7T0608I1B2bSVnbi2cwCrmOD1Yw==", + "license": "MIT", "dependencies": { "@inquirer/core": "^10.1.0", "@inquirer/type": "^3.0.1", @@ -1464,6 +1531,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.2.tgz", "integrity": "sha512-Zv4FC7w4dJ13BOJfKRQCICQfShinGjb1bCEIHxTSnjj2telu3+3RHwHubPG9HyD4aix5s+lyAMEK/wSFD75HLA==", + "license": "MIT", "dependencies": { "@inquirer/core": "^10.1.0", "@inquirer/figures": "^1.0.8", @@ -1481,6 +1549,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.0.2.tgz", "integrity": "sha512-uSWUzaSYAEj0hlzxa1mUB6VqrKaYx0QxGBLZzU4xWFxaSyGaXxsSE4OSOwdU24j0xl8OajgayqFXW0l2bkl2kg==", + "license": "MIT", "dependencies": { "@inquirer/core": "^10.1.0", "@inquirer/figures": "^1.0.8", @@ -1499,6 +1568,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.1.tgz", "integrity": "sha512-+ksJMIy92sOAiAccGpcKZUc3bYO07cADnscIxHBknEm3uNts3movSmBofc1908BNy5edKscxYeAdaX1NXkHS6A==", + "license": "MIT", "engines": { "node": ">=18" }, @@ -1786,12 +1856,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@microsoft/tsdoc-config/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2534,6 +2598,7 @@ "version": "22.9.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.1.tgz", "integrity": "sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg==", + "license": "MIT", "dependencies": { "undici-types": "~6.19.8" } @@ -2635,7 +2700,8 @@ "node_modules/@types/triple-beam": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" }, "node_modules/@types/unist": { "version": "3.0.2", @@ -2650,6 +2716,11 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/validator": { + "version": "13.12.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.2.tgz", + "integrity": "sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==" + }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -2672,6 +2743,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.15.0.tgz", "integrity": "sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.15.0", @@ -2705,6 +2777,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.15.0.tgz", "integrity": "sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/scope-manager": "8.15.0", "@typescript-eslint/types": "8.15.0", @@ -2733,6 +2806,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz", "integrity": "sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.15.0", "@typescript-eslint/visitor-keys": "8.15.0" @@ -2750,6 +2824,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.15.0.tgz", "integrity": "sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/typescript-estree": "8.15.0", "@typescript-eslint/utils": "8.15.0", @@ -2777,6 +2852,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.15.0.tgz", "integrity": "sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -2790,6 +2866,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz", "integrity": "sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "8.15.0", "@typescript-eslint/visitor-keys": "8.15.0", @@ -2818,6 +2895,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -2827,6 +2905,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -2842,6 +2921,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.15.0.tgz", "integrity": "sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.15.0", @@ -2869,6 +2949,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.15.0.tgz", "integrity": "sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.15.0", "eslint-visitor-keys": "^4.2.0" @@ -2886,6 +2967,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -2946,21 +3028,6 @@ "node": ">=12.0" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -2973,6 +3040,7 @@ "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", "dependencies": { "type-fest": "^0.21.3" }, @@ -3224,9 +3292,9 @@ "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", + "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -3597,7 +3665,8 @@ "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "license": "MIT" }, "node_modules/charenc": { "version": "0.0.2", @@ -3681,6 +3750,21 @@ "node": ">=8" } }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + }, + "node_modules/class-validator": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", + "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" + } + }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -3760,6 +3844,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", "engines": { "node": ">= 12" } @@ -3947,6 +4032,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4259,9 +4345,9 @@ } }, "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", - "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==" + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -4482,6 +4568,7 @@ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -4537,6 +4624,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -4795,6 +4883,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.13.2.tgz", "integrity": "sha512-MhBAKkT01h8cOXcTBTlpuR7bxH5OBUNpUXefsvwSVEy46cY4m/Kzr2osUCQvA3zJFD6KuCeNNDv0+HDuWk/OcA==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.1", "enhanced-resolve": "^5.17.1", @@ -4923,6 +5012,22 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4963,6 +5068,12 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "node_modules/esm": { "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", @@ -5071,6 +5182,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "license": "MIT", "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", @@ -5089,9 +5201,9 @@ ] }, "node_modules/fast-copy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", - "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.1.tgz", + "integrity": "sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA==" }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -5140,9 +5252,9 @@ "dev": true }, "node_modules/fast-redact": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", - "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz", + "integrity": "sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==", "engines": { "node": ">=6" } @@ -5179,7 +5291,8 @@ "node_modules/fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" }, "node_modules/figlet": { "version": "1.8.0", @@ -5267,9 +5380,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -5329,9 +5442,9 @@ } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -5368,6 +5481,7 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -5612,7 +5726,8 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/har-schema": { "version": "2.0.0", @@ -5635,6 +5750,26 @@ "node": ">=6" } }, + "node_modules/har-validator/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/har-validator/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -5772,9 +5907,50 @@ } }, "node_modules/help-me": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", - "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-4.2.0.tgz", + "integrity": "sha512-TAOnTB8Tz5Dw8penUuzHVrKNKlCIbwwbHnXraNJxPwf8LRtE2HlM84RYuezMFcwOJmoYOCWVDyJ8TQGxn9PgxA==", + "dependencies": { + "glob": "^8.0.0", + "readable-stream": "^3.6.0" + } + }, + "node_modules/help-me/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/help-me/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/help-me/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } }, "node_modules/hmac-drbg": { "version": "1.0.1", @@ -5837,6 +6013,7 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -5940,6 +6117,7 @@ "version": "12.1.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.1.0.tgz", "integrity": "sha512-3/iexk64jn3EF0tjWYfOJi/DNWtcSSwg4ER67cnopO2xlCTUQjFCTMH9NBNQAMO/+dC3sEvKJtGJsqXnCkSsFg==", + "license": "MIT", "dependencies": { "@inquirer/core": "^10.1.0", "@inquirer/prompts": "^7.1.0", @@ -6597,9 +6775,10 @@ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -6625,9 +6804,9 @@ } }, "node_modules/jsonpath-plus": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.1.0.tgz", - "integrity": "sha512-gHfV1IYqH8uJHYVTs8BJX1XKy2/rR93+f8QQi0xhx95aCiXn1ettYAd5T+7FU6wfqyDoX/wy0pm/fL3jOKJ9Lg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.0.0.tgz", + "integrity": "sha512-v7j76HGp/ibKlXYeZ7UrfCLSNDaBWuJMA0GaMjA4sZJtCtY89qgPyToDDcl2zdeHh4B5q/B3g2pQdW76fOg/dA==", "dependencies": { "@jsep-plugin/assignment": "^1.2.1", "@jsep-plugin/regex": "^1.0.3", @@ -6696,6 +6875,11 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.11.12", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.12.tgz", + "integrity": "sha512-QkJn9/D7zZ1ucvT++TQSvZuSA2xAWeUytU+DiEQwbPKLyrDpvbul2AFs1CGbRAPpSCCk47aRAb5DX5mmcayp4g==" + }, "node_modules/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", @@ -7017,6 +7201,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", @@ -8216,6 +8401,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "license": "ISC", "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -8468,6 +8654,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -8652,39 +8839,39 @@ } }, "node_modules/pino": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", - "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==", + "version": "8.16.2", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.16.2.tgz", + "integrity": "sha512-2advCDGVEvkKu9TTVSa/kWW7Z3htI/sBKEZpqiHk6ive0i/7f5b1rsU8jn0aimxqfnSz5bj/nOYkwhBUn5xxvg==", "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^1.2.0", + "pino-abstract-transport": "v1.1.0", "pino-std-serializers": "^6.0.0", - "process-warning": "^3.0.0", + "process-warning": "^2.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^3.7.0", - "thread-stream": "^2.6.0" + "thread-stream": "^2.0.0" }, "bin": { "pino": "bin.js" } }, "node_modules/pino-abstract-transport": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", - "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz", + "integrity": "sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==", "dependencies": { "readable-stream": "^4.0.0", "split2": "^4.0.0" } }, "node_modules/pino-abstract-transport/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz", + "integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==", "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", @@ -8697,15 +8884,15 @@ } }, "node_modules/pino-pretty": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.3.1.tgz", - "integrity": "sha512-az8JbIYeN/1iLj2t0jR9DV48/LQ3RC6hZPpapKPkb84Q+yTidMCpgWxIT3N0flnBDilyBQ1luWNpOeJptjdp/g==", + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.2.3.tgz", + "integrity": "sha512-4jfIUc8TC1GPUfDyMSlW1STeORqkoxec71yhxIpLDQapUu8WOuoz2TTCoidrIssyz78LZC69whBMPIKCMbi3cw==", "dependencies": { "colorette": "^2.0.7", "dateformat": "^4.6.3", "fast-copy": "^3.0.0", "fast-safe-stringify": "^2.1.1", - "help-me": "^5.0.0", + "help-me": "^4.0.1", "joycon": "^3.1.1", "minimist": "^1.2.6", "on-exit-leak-free": "^2.1.0", @@ -8721,9 +8908,9 @@ } }, "node_modules/pino-pretty/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz", + "integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==", "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", @@ -8785,9 +8972,9 @@ } }, "node_modules/process-warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", - "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz", + "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==" }, "node_modules/property-information": { "version": "6.5.0", @@ -10140,9 +10327,9 @@ } }, "node_modules/sonic-boom": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", - "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.7.0.tgz", + "integrity": "sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg==", "dependencies": { "atomic-sleep": "^1.0.0" } @@ -10510,9 +10697,9 @@ "dev": true }, "node_modules/thread-stream": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", - "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz", + "integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==", "dependencies": { "real-require": "^0.2.0" } @@ -10521,6 +10708,7 @@ "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "license": "MIT", "dependencies": { "os-tmpdir": "~1.0.2" }, @@ -10566,6 +10754,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", "engines": { "node": ">= 14.0.0" } @@ -10581,9 +10770,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", + "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", "dev": true, "license": "MIT", "engines": { @@ -10638,6 +10827,7 @@ "version": "4.19.2", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "license": "MIT", "dependencies": { "esbuild": "~0.23.0", "get-tsconfig": "^4.7.5" @@ -10710,6 +10900,7 @@ "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -10861,6 +11052,7 @@ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.15.0.tgz", "integrity": "sha512-wY4FRGl0ZI+ZU4Jo/yjdBu0lVTSML58pu6PgGtJmCufvzfV565pUF6iACQt092uFOd49iLOTX/sEVmHtbSrS+w==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/eslint-plugin": "8.15.0", "@typescript-eslint/parser": "8.15.0", @@ -11261,6 +11453,7 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { "uuid": "dist/esm/bin/uuid" } @@ -11279,6 +11472,14 @@ "node": ">=10.12.0" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -11504,6 +11705,7 @@ "version": "3.17.0", "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "license": "MIT", "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", @@ -11525,6 +11727,7 @@ "version": "4.9.0", "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", "dependencies": { "logform": "^2.7.0", "readable-stream": "^3.6.2", @@ -11545,6 +11748,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -11620,6 +11824,7 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", + "license": "ISC", "bin": { "yaml": "bin.mjs" }, @@ -11720,6 +11925,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "license": "MIT", "engines": { "node": ">=18" }, diff --git a/package.json b/package.json index 9fd71e21c..049dea9c0 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,8 @@ "@peculiar/x509": "^1.12.3", "adm-zip": "^0.5.16", "chalk": "^5.3.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "dot-object": "^2.1.5", "dotenv": "^16.4.5", "enquirer": "^2.4.1", diff --git a/src/commands/base.ts b/src/commands/base.ts index 96e95630d..a4c1c0a48 100644 --- a/src/commands/base.ts +++ b/src/commands/base.ts @@ -20,6 +20,7 @@ import { MissingArgumentError } from '../core/errors.js' import { ShellRunner } from '../core/shell_runner.js' import type { ChartManager, ConfigManager, Helm, K8, DependencyManager, LeaseManager } from '../core/index.js' import type { CommandFlag, Opts } from '../types/index.js' +import { type LocalConfig } from '../core/config/local_config.js' export class BaseCommand extends ShellRunner { protected readonly helm: Helm @@ -29,6 +30,7 @@ export class BaseCommand extends ShellRunner { protected readonly depManager: DependencyManager protected readonly leaseManager: LeaseManager protected readonly _configMaps = new Map() + protected readonly localConfig: LocalConfig constructor (opts: Opts) { if (!opts || !opts.logger) throw new Error('An instance of core/SoloLogger is required') @@ -37,6 +39,7 @@ export class BaseCommand extends ShellRunner { if (!opts || !opts.chartManager) throw new Error('An instance of core/ChartManager is required') if (!opts || !opts.configManager) throw new Error('An instance of core/ConfigManager is required') if (!opts || !opts.depManager) throw new Error('An instance of core/DependencyManager is required') + if (!opts || !opts.localConfig) throw new Error('An instance of core/LocalConfig is required') super(opts.logger) @@ -46,6 +49,7 @@ export class BaseCommand extends ShellRunner { this.configManager = opts.configManager this.depManager = opts.depManager this.leaseManager = opts.leaseManager + this.localConfig = opts.localConfig } async prepareChartPath (chartDir: string, chartRepo: string, chartReleaseName: string) { diff --git a/src/commands/flags.ts b/src/commands/flags.ts index 69795cba8..8e3fcb6df 100644 --- a/src/commands/flags.ts +++ b/src/commands/flags.ts @@ -708,6 +708,36 @@ export const hederaExplorerVersion: CommandFlag = { } } +export const userEmailAddress: CommandFlag = { + constName: 'userEmailAddress', + name: 'email', + definition: { + describe: 'User email address used for local configuration', + defaultValue: '', + type: 'string' + } +} + +export const deploymentName: CommandFlag = { + constName: 'deploymentName', + name: 'deployment-name', + definition: { + describe: 'Solo deployment name', + defaultValue: '', + type: 'string' + } +} + +export const deploymentClusters: CommandFlag = { + constName: 'deploymentClusters', + name: 'deployment-clusters', + definition: { + describe: 'Solo deployment cluster list (comma separated)', + defaultValue: '', + type: 'string' + } +} + //* ------------- Node Proxy Certificates ------------- !// export const grpcTlsCertificatePath: CommandFlag = { @@ -782,6 +812,8 @@ export const allFlags: CommandFlag[] = [ deployCertManagerCrds, deployHederaExplorer, deployJsonRpcRelay, + deploymentClusters, + deploymentName, deployMinio, deployPrometheusStack, devMode, @@ -823,11 +855,10 @@ export const allFlags: CommandFlag[] = [ tlsPrivateKey, tlsPublicKey, updateAccountKeys, + userEmailAddress, valuesFile, mirrorNodeVersion, hederaExplorerVersion, - inputDir, - outputDir, grpcTlsCertificatePath, grpcWebTlsCertificatePath, grpcTlsKeyPath, diff --git a/src/commands/prompts.ts b/src/commands/prompts.ts index ff8a175be..53037bbbc 100644 --- a/src/commands/prompts.ts +++ b/src/commands/prompts.ts @@ -23,6 +23,7 @@ import * as helpers from '../core/helpers.js' import { hederaExplorerVersion, resetDisabledPrompts } from './flags.js' import type { ListrTaskWrapper } from 'listr2' import { type CommandFlag } from '../types/index.js' +import validator from 'validator' async function prompt (type: string, task: ListrTaskWrapper, input: any, defaultValue: any, promptMessage: string, emptyCheckMessage: string | null, flagName: string) { try { @@ -366,6 +367,38 @@ export async function promptUpdateAccountKeys (task: ListrTaskWrapper, input: any) { + const promptForInput = async () => { + return await task.prompt(ListrEnquirerPromptAdapter).run({ + type: 'text', + message: 'Please enter your email address:' + }) + } + + input = await promptForInput() + while (!validator.isEmail(input)) { + input = await promptForInput() + } + + return input +} + +export async function promptDeploymentName (task: ListrTaskWrapper, input: any) { + return await promptText(task, input, + flags.deploymentName.definition.defaultValue, + 'Enter the Solo deployment name: ', + null, + flags.deploymentName.name) +} + +export async function promptDeploymentClusters (task: ListrTaskWrapper, input: any) { + return await promptText(task, input, + flags.deploymentClusters.definition.defaultValue, + 'Enter the Solo deployment cluster names (comma separated): ', + null, + flags.deploymentClusters.name) +} + export async function promptPrivateKey (task: ListrTaskWrapper, input: any) { return await promptText(task, input, flags.ed25519PrivateKey.definition.defaultValue, @@ -531,6 +564,7 @@ export function getPromptMap (): Map { .set(flags.replicaCount.name, promptReplicaCount) .set(flags.tlsClusterIssuerType.name, promptTlsClusterIssuerType) .set(flags.updateAccountKeys.name, promptUpdateAccountKeys) + .set(flags.userEmailAddress.name, promptUserEmailAddress) .set(flags.valuesFile.name, promptValuesFile) .set(flags.nodeAlias.name, promptNewNodeAlias) .set(flags.gossipEndpoints.name, promptGossipEndpoints) diff --git a/src/core/config/local_config.ts b/src/core/config/local_config.ts new file mode 100644 index 000000000..75231f3e6 --- /dev/null +++ b/src/core/config/local_config.ts @@ -0,0 +1,146 @@ +/** + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the ""License""); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an ""AS IS"" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { IsEmail, IsNotEmpty, IsObject, IsString, validateSync } from 'class-validator' +import { type ListrTask } from 'listr2' +import fs from 'fs' +import * as yaml from 'yaml' +import { flags } from '../../commands/index.js' +import { type Deployment, type Deployments, type LocalConfigData } from './local_config_data.js' +import { MissingArgumentError, SoloError } from '../errors.js' +import { promptDeploymentClusters, promptDeploymentName, promptUserEmailAddress } from '../../commands/prompts.js' +import { type SoloLogger } from '../logging.js' +import { Task } from '../task.js' +import { IsDeployments } from '../validator_decorators.js' + +export class LocalConfig implements LocalConfigData { + @IsNotEmpty() + @IsEmail() + userEmailAddress: string + + // The string is the name of the deployment, will be used as the namespace, + // so it needs to be available in all targeted clusters + @IsNotEmpty() + @IsObject() + @IsDeployments() + deployments: Deployments + + @IsNotEmpty() + @IsString() + currentDeploymentName : string + + private readonly skipPromptTask: boolean = false + + constructor (private readonly filePath: string, private readonly logger: SoloLogger) { + if (!filePath || filePath === '') throw new MissingArgumentError('a valid filePath is required') + if (!logger) throw new Error('An instance of core/SoloLogger is required') + + const allowedKeys = ['userEmailAddress', 'deployments', 'currentDeploymentName'] + if (this.configFileExists()) { + const fileContent = fs.readFileSync(filePath, 'utf8') + const parsedConfig = yaml.parse(fileContent) + + for(const key in parsedConfig) { + if (!allowedKeys.includes(key)) { + throw new SoloError('Validation of local config failed') + } + this[key] = parsedConfig[key] + } + + this.validate() + this.skipPromptTask = true + } + } + + private validate () { + const genericMessage = 'Validation of local config failed' + const errors = validateSync(this, {}) + + if (errors.length) { + throw new SoloError(genericMessage) + } + + try { + // Custom validations: + if (!this.deployments[this.currentDeploymentName]) { + throw new SoloError(genericMessage) + } + } + catch(e: any) { throw new SoloError(genericMessage) } + } + + public setUserEmailAddress (emailAddress: string): this { + this.userEmailAddress = emailAddress + this.validate() + return this + } + + public setDeployments (deployments: Deployments): this { + this.deployments = deployments + this.validate() + return this + } + + public setCurrentDeployment (deploymentName: string): this { + this.currentDeploymentName = deploymentName + this.validate() + return this + } + + public getCurrentDeployment (): Deployment { + return this.deployments[this.currentDeploymentName] + } + + private configFileExists (): boolean { + return fs.existsSync(this.filePath) + } + + public async write (): Promise { + const yamlContent = yaml.stringify({ + userEmailAddress: this.userEmailAddress, + deployments: this.deployments, + currentDeploymentName: this.currentDeploymentName + }) + await fs.promises.writeFile(this.filePath, yamlContent) + this.logger.info(`Wrote local config to ${this.filePath}`) + } + + public promptLocalConfigTask (k8, argv): ListrTask[] { + return new Task('Prompt local configuration', async (ctx, task) => { + let userEmailAddress = argv[flags.userEmailAddress.name] + if (!userEmailAddress) userEmailAddress = await promptUserEmailAddress(task, userEmailAddress) + + let deploymentName = argv[flags.deploymentName.name] + if (!deploymentName) deploymentName = await promptDeploymentName(task, deploymentName) + + let deploymentClusters = argv[flags.deploymentClusters.name] + if (!deploymentClusters) deploymentClusters = await promptDeploymentClusters(task, deploymentClusters) + + const deployments = {} + deployments[deploymentName] = { + clusterAliases: deploymentClusters.split(',') + } + + this.userEmailAddress = userEmailAddress + this.deployments = deployments + this.currentDeploymentName = deploymentName + this.validate() + await this.write() + + return this + }, this.skipPromptTask) as ListrTask[] + } +} diff --git a/src/core/config/local_config_data.ts b/src/core/config/local_config_data.ts new file mode 100644 index 000000000..2fe4c8403 --- /dev/null +++ b/src/core/config/local_config_data.ts @@ -0,0 +1,30 @@ +/** + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the ""License""); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an ""AS IS"" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +export interface Deployment { + clusterAliases : string[] +} + +// an alias for the cluster, provided during the configuration +// of the deployment, must be unique +export type Deployments = Record; + +export interface LocalConfigData { + userEmailAddress: string; + deployments: Deployments; + currentDeploymentName: string; +} \ No newline at end of file diff --git a/src/core/constants.ts b/src/core/constants.ts index 7f8bad2b9..af485d0de 100644 --- a/src/core/constants.ts +++ b/src/core/constants.ts @@ -178,3 +178,5 @@ export const RELAY_PODS_RUNNING_DELAY = +process.env.RELAY_PODS_RUNNING_DELAY || export const RELAY_PODS_READY_MAX_ATTEMPTS = +process.env.RELAY_PODS_READY_MAX_ATTEMPTS || 100 export const RELAY_PODS_READY_DELAY = +process.env.RELAY_PODS_READY_DELAY || 1_000 + +export const DEFAULT_LOCAL_CONFIG_FILE = 'local-config.yaml' diff --git a/src/core/index.ts b/src/core/index.ts index 122fae85e..7eaeddc47 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -33,6 +33,7 @@ import { DependencyManager } from './dependency_managers/index.js' import { AccountManager } from './account_manager.js' import { LeaseManager } from './lease/lease_manager.js' import { CertificateManager } from './certificate_manager.js' +import { LocalConfig } from './config/local_config.js' // Expose components from the core module export { @@ -54,5 +55,6 @@ export { DependencyManager, AccountManager, LeaseManager, - CertificateManager + CertificateManager, + LocalConfig } diff --git a/src/core/validator_decorators.ts b/src/core/validator_decorators.ts new file mode 100644 index 000000000..ec8187e38 --- /dev/null +++ b/src/core/validator_decorators.ts @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the ""License""); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an ""AS IS"" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { registerDecorator, type ValidationOptions, type ValidationArguments } from 'class-validator' + +const isObject = (obj) => obj === Object(obj) + +export const IsDeployments = (validationOptions?: ValidationOptions) => { + return function (object: any, propertyName: string) { + registerDecorator({ + name: 'IsDeployments', + target: object.constructor, + propertyName: propertyName, + constraints: [], + options: { + message: 'Wrong deployments format', + ...validationOptions, + }, + validator: { + validate (value: any, args: ValidationArguments) { + if (!isObject(value)) return false + if (Object.keys(value).length === 0) return true + + const keys = Object.keys(value) + + return keys.every(key => { + if (typeof key !== 'string') return false + if (!isObject(value[key])) return false + if (!Array.isArray(value[key].clusterAliases)) return false + if (!value[key].clusterAliases.every(val => typeof val === 'string')) return false + + return true + }) + }, + }, + }) + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 018af247b..935368943 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,7 +22,7 @@ import * as commands from './commands/index.js' import { HelmDependencyManager, DependencyManager } from './core/dependency_managers/index.js' import { ChartManager, ConfigManager, PackageDownloader, PlatformInstaller, Helm, logging, - KeyManager, Zippy, constants, ProfileManager, AccountManager, LeaseManager, CertificateManager, helpers + KeyManager, Zippy, constants, ProfileManager, AccountManager, LeaseManager, CertificateManager, helpers, LocalConfig } from './core/index.js' import 'dotenv/config' import { K8 } from './core/k8.js' @@ -30,6 +30,7 @@ import { ListrLogger } from 'listr2' import { CustomProcessOutput } from './core/process_output.js' import { type Opts } from './types/index.js' import { IntervalLeaseRenewalService, type LeaseRenewalService } from './core/lease/lease_renewal.js' +import path from 'path' export function main (argv: any) { const logger = logging.NewLogger('debug') @@ -61,6 +62,7 @@ export function main (argv: any) { const leaseRenewalService: LeaseRenewalService = new IntervalLeaseRenewalService() const leaseManager = new LeaseManager(k8, configManager, logger, leaseRenewalService) const certificateManager = new CertificateManager(k8, logger, configManager) + const localConfig = new LocalConfig(path.join(constants.SOLO_CACHE_DIR, constants.DEFAULT_LOCAL_CONFIG_FILE), logger) // set cluster and namespace in the global configManager from kubernetes context // so that we don't need to prompt the user @@ -68,6 +70,7 @@ export function main (argv: any) { const context = kubeConfig.getContextObject(kubeConfig.getCurrentContext()) const cluster = kubeConfig.getCurrentCluster() + const opts: Opts = { logger, helm, @@ -82,6 +85,7 @@ export function main (argv: any) { profileManager, leaseManager, certificateManager, + localConfig } const processArguments = (argv: any, yargs: any) => { diff --git a/src/types/index.ts b/src/types/index.ts index 006765792..f3aa92581 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -21,7 +21,8 @@ import type crypto from 'crypto' import type { SoloLogger } from '../core/logging.js' import type { ChartManager, ConfigManager, Helm, K8, KeyManager, PackageDownloader, PlatformInstaller, - ProfileManager, DependencyManager, AccountManager, LeaseManager, CertificateManager + ProfileManager, DependencyManager, AccountManager, LeaseManager, CertificateManager, + LocalConfig } from '../core/index.js' export interface NodeKeyObject { @@ -78,6 +79,7 @@ export interface Opts { keyManager: KeyManager accountManager: AccountManager profileManager: ProfileManager - leaseManager: LeaseManager + leaseManager: LeaseManager, certificateManager: CertificateManager + localConfig: LocalConfig } diff --git a/test/data/local-config.yaml b/test/data/local-config.yaml new file mode 100644 index 000000000..83005650b --- /dev/null +++ b/test/data/local-config.yaml @@ -0,0 +1,8 @@ +userEmailAddress: joe@doe.com +deployments: + deployment-1: + clusterAliases: + - cluster-1 +currentDeploymentName: deployment-1 +clusterMappings: + kind-node-add: kind-node-add diff --git a/test/e2e/integration/commands/init.test.ts b/test/e2e/integration/commands/init.test.ts index ae5b316f4..07e1802e2 100644 --- a/test/e2e/integration/commands/init.test.ts +++ b/test/e2e/integration/commands/init.test.ts @@ -23,11 +23,13 @@ import { DependencyManager } from '../../../../src/core/dependency_managers/index.js' import { - ChartManager, ConfigManager, constants, Helm, K8, KeyManager, LeaseManager, logging, PackageDownloader, Zippy + ChartManager, ConfigManager, constants, Helm, K8, KeyManager, LeaseManager, logging, PackageDownloader, Zippy, LocalConfig, } from '../../../../src/core/index.js' import { SECONDS } from '../../../../src/core/constants.js' import sinon from 'sinon' import { IntervalLeaseRenewalService } from '../../../../src/core/lease/lease_renewal.js' +import path from 'path' +import { BASE_TEST_DIR } from '../../../test_util.js' const testLogger = logging.NewLogger('debug', true) describe('InitCommand', () => { @@ -42,6 +44,7 @@ describe('InitCommand', () => { const helm = new Helm(testLogger) const chartManager = new ChartManager(helm, testLogger) const configManager = new ConfigManager(testLogger) + const localConfig = new LocalConfig(path.join(BASE_TEST_DIR, 'local-config.yaml'), testLogger) const keyManager = new KeyManager(testLogger) @@ -58,7 +61,7 @@ describe('InitCommand', () => { leaseManager = new LeaseManager(k8, configManager, testLogger, new IntervalLeaseRenewalService()) // @ts-ignore initCmd = new InitCommand({ - logger: testLogger, helm, k8, chartManager, configManager, depManager, keyManager, leaseManager + logger: testLogger, helm, k8, chartManager, configManager, depManager, keyManager, leaseManager, localConfig }) }) diff --git a/test/test_util.ts b/test/test_util.ts index e63afb0c4..d30e0f399 100644 --- a/test/test_util.ts +++ b/test/test_util.ts @@ -45,7 +45,7 @@ import { ProfileManager, Templates, Zippy, - AccountManager, CertificateManager + AccountManager, CertificateManager, LocalConfig } from '../src/core/index.js' import { AccountBalanceQuery, @@ -62,6 +62,7 @@ import type { SoloLogger } from '../src/core/logging.js' import type { BaseCommand } from '../src/commands/base.js' import type { NodeAlias } from '../src/types/aliases.js' import type { NetworkNodeServices } from '../src/core/network_node_services.js' +import sinon from 'sinon' import { HEDERA_PLATFORM_VERSION } from '../version.js' import { IntervalLeaseRenewalService } from '../src/core/lease/lease_renewal.js' @@ -69,9 +70,10 @@ export const testLogger = logging.NewLogger('debug', true) export const TEST_CLUSTER = 'solo-e2e' export const HEDERA_PLATFORM_VERSION_TAG = HEDERA_PLATFORM_VERSION +export const BASE_TEST_DIR = path.join('test', 'data', 'tmp') + export function getTestCacheDir (testName?: string) { - const baseDir = 'test/data/tmp' - const d = testName ? path.join(baseDir, testName) : baseDir + const d = testName ? path.join(BASE_TEST_DIR, testName) : BASE_TEST_DIR if (!fs.existsSync(d)) { fs.mkdirSync(d, { recursive: true }) @@ -108,6 +110,7 @@ interface TestOpts { profileManager: ProfileManager leaseManager: LeaseManager certificateManager: CertificateManager + localConfig: LocalConfig } interface BootstrapResponse { @@ -137,7 +140,6 @@ export function bootstrapTestVariables ( const cacheDir: string = argv[flags.cacheDir.name] || getTestCacheDir(testName) const configManager = new ConfigManager(testLogger) configManager.update(argv) - const downloader = new PackageDownloader(testLogger) const zippy = new Zippy(testLogger) const helmDepManager = new HelmDependencyManager(downloader, zippy, testLogger) @@ -152,6 +154,7 @@ export function bootstrapTestVariables ( const profileManager = new ProfileManager(testLogger, configManager) const leaseManager = new LeaseManager(k8, configManager, testLogger, new IntervalLeaseRenewalService()) const certificateManager = new CertificateManager(k8, testLogger, configManager) + const localConfig = new LocalConfig(path.join(BASE_TEST_DIR, 'local-config.yaml'), testLogger) const opts: TestOpts = { logger: testLogger, @@ -167,7 +170,8 @@ export function bootstrapTestVariables ( cacheDir, profileManager, leaseManager, - certificateManager + certificateManager, + localConfig } const initCmd = initCmdArg || new InitCommand(opts) diff --git a/test/unit/commands/base.test.ts b/test/unit/commands/base.test.ts index e49693e35..10d47dcb6 100644 --- a/test/unit/commands/base.test.ts +++ b/test/unit/commands/base.test.ts @@ -23,11 +23,13 @@ import { Helm, logging, PackageDownloader, Zippy, constants, - K8 + K8, LocalConfig } from '../../../src/core/index.js' import { BaseCommand } from '../../../src/commands/base.js' import * as flags from '../../../src/commands/flags.js' import sinon from 'sinon' +import path from 'path' +import { BASE_TEST_DIR } from '../../test_util.js' const testLogger = logging.NewLogger('debug', true) @@ -42,6 +44,7 @@ describe('BaseCommand', () => { const helmDepManager = new HelmDependencyManager(downloader, zippy, testLogger) const depManagerMap = new Map().set(constants.HELM, helmDepManager) const depManager = new DependencyManager(testLogger, depManagerMap) + const localConfig = new LocalConfig(path.join(BASE_TEST_DIR, 'local-config.yaml'), testLogger) let sandbox = sinon.createSandbox() @@ -60,7 +63,8 @@ describe('BaseCommand', () => { k8, chartManager, configManager, - depManager + depManager, + localConfig }) }) diff --git a/test/unit/commands/node.test.ts b/test/unit/commands/node.test.ts index efd93ccc4..2ea0ea9d4 100644 --- a/test/unit/commands/node.test.ts +++ b/test/unit/commands/node.test.ts @@ -26,7 +26,8 @@ const getBaseCommandOpts = () => ({ k8: sinon.stub(), chartManager: sinon.stub(), configManager: sinon.stub(), - depManager: sinon.stub() + depManager: sinon.stub(), + localConfig: sinon.stub() }) describe('NodeCommand unit tests', () => { diff --git a/test/unit/core/local_config.test.ts b/test/unit/core/local_config.test.ts new file mode 100644 index 000000000..27128a616 --- /dev/null +++ b/test/unit/core/local_config.test.ts @@ -0,0 +1,228 @@ +/** + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the ""License""); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an ""AS IS"" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +import { LocalConfig } from '../../../src/core/config/local_config.js' +import fs from 'fs' +import { stringify } from 'yaml' +import { expect } from 'chai' +import { MissingArgumentError, SoloError } from '../../../src/core/errors.js' +import { getTestCacheDir, testLogger } from '../../test_util.js' + +describe('LocalConfig', () => { + let localConfig + const filePath = `${getTestCacheDir('LocalConfig')}/localConfig.yaml` + const config = { + userEmailAddress: 'john.doe@example.com', + deployments: { + 'my-deployment': { + clusterAliases: ['cluster-1', 'context-1'], + }, + 'my-other-deployment': { + clusterAliases: ['cluster-2', 'context-2'], + } + }, + currentDeploymentName: 'my-deployment' + } + + + const expectFailedValidation = () => { + try { + new LocalConfig(filePath, testLogger) + expect.fail('Expected an error to be thrown') + } + catch(error) { + expect(error).to.be.instanceOf(SoloError) + expect(error.message).to.equal('Validation of local config failed') + } + } + + beforeEach(async () => { + await fs.promises.writeFile(filePath, stringify(config)) + localConfig = new LocalConfig(filePath, testLogger) + }) + + afterEach(async () => { + await fs.promises.unlink(filePath) + }) + + it('should load config from file', async () => { + expect(localConfig.userEmailAddress).to.eq(config.userEmailAddress) + expect(localConfig.deployments).to.deep.eq(config.deployments) + expect(localConfig.currentDeploymentName).to.eq(config.currentDeploymentName) + }) + + it('should set user email address', async () => { + const newEmailAddress = 'jane.doe@example.com' + localConfig.setUserEmailAddress(newEmailAddress) + expect(localConfig.userEmailAddress).to.eq(newEmailAddress) + + await localConfig.write() + + // reinitialize with updated config file + const newConfig = new LocalConfig(filePath, testLogger) + expect(newConfig.userEmailAddress).to.eq(newEmailAddress) + }) + + it('should not set an invalid email as user email address', async () => { + try { + localConfig.setUserEmailAddress('invalidEmail') + expect.fail('expected an error to be thrown') + } catch (error) { + expect(error).to.be.instanceOf(SoloError) + } + }) + + it('should set deployments', async () => { + const newDeployments = { + 'my-deployment': { + clusterAliases: ['cluster-1', 'context-1'], + }, + 'my-new-deployment': { + clusterAliases: ['cluster-3', 'context-3'], + } + } + + localConfig.setDeployments(newDeployments) + expect(localConfig.deployments).to.deep.eq(newDeployments) + + await localConfig.write() + const newConfig = new LocalConfig(filePath, testLogger) + expect(newConfig.deployments).to.deep.eq(newDeployments) + }) + + it('should not set invalid deployments', async () => { + const validDeployment = { clusterAliases: ['cluster-3', 'cluster-4'] } + const invalidDeployments = [ + { foo: ['bar'] }, + { clusterAliases: [5, 6, 7] }, + { clusterAliases: 'bar' }, + { clusterAliases: 5 }, + { clusterAliases: { foo: 'bar ' } } + ] + + for ( const invalidDeployment of invalidDeployments ) { + const deployments = { + 'my-deployment': validDeployment, + 'invalid-deployment': invalidDeployment + } + + try { + localConfig.setDeployments(deployments) + expect.fail('expected an error to be thrown') + } catch (error) { + expect(error).to.be.instanceOf(SoloError) + } + } + }) + + it('should not set invalid context mappings', async () => { + const invalidContextMappings = { + 'cluster-3': 'context-3', + 'invalid-cluster': 5, + } + + try { + localConfig.setContextMappings(invalidContextMappings) + expect.fail('expected an error to be thrown') + } catch (error) { + expect(error).to.be.instanceOf(TypeError) + } + }) + + it('should get current deployment', async () => { + expect(localConfig.getCurrentDeployment()).to.deep.eq(config.deployments[config.currentDeploymentName]) + }) + + it('should set current deployment', async () => { + const newCurrentDeployment = 'my-other-deployment' + localConfig.setCurrentDeployment(newCurrentDeployment) + + expect(localConfig.currentDeploymentName).to.eq(newCurrentDeployment) + + await localConfig.write() + const newConfig = new LocalConfig(filePath, testLogger) + expect(newConfig.currentDeploymentName).to.eq(newCurrentDeployment) + }) + + it('should not set invalid or non-existent current deployment', async () => { + const invalidCurrentDeploymentName = 5 + try { + localConfig.setCurrentDeployment(invalidCurrentDeploymentName) + expect.fail('expected an error to be thrown') + } catch (error) { + expect(error).to.be.instanceOf(SoloError) + } + + const nonExistentCurrentDeploymentName = 'non-existent-deployment' + try { + localConfig.setCurrentDeployment(nonExistentCurrentDeploymentName) + expect.fail('expected an error to be thrown') + } catch (error) { + expect(error).to.be.instanceOf(SoloError) + } + }) + + it('should throw an error if file path is not set', async () => { + try { + new LocalConfig('', testLogger) + expect.fail('Expected an error to be thrown') + } catch (error) { + expect(error).to.be.instanceOf(MissingArgumentError) + expect(error.message).to.equal('a valid filePath is required') + } + }) + + it('should throw a validation error if the config file is not a valid LocalConfig', async () => { + // without any known properties + await fs.promises.writeFile(filePath, 'foo: bar') + expectFailedValidation() + + // with extra property + await fs.promises.writeFile(filePath, stringify({ ...config, foo: 'bar' })) + expectFailedValidation() + }) + + it('should throw a validation error if userEmailAddress is not a valid email', async () => { + await fs.promises.writeFile(filePath, stringify({ ...config, userEmailAddress: 'foo' })) + expectFailedValidation() + + await fs.promises.writeFile(filePath, stringify({ ...config, userEmailAddress: 5 })) + expectFailedValidation() + }) + + it('should throw a validation error if deployments format is not correct', async () => { + await fs.promises.writeFile(filePath, stringify({ ...config, deployments: 'foo' })) + expectFailedValidation() + + await fs.promises.writeFile(filePath, stringify({ ...config, deployments: { 'foo': 'bar' } })) + expectFailedValidation() + + await fs.promises.writeFile(filePath, stringify({ + ...config, + deployments: [{ 'foo': 'bar' }] + }) + ) + expectFailedValidation() + }) + + it('should throw a validation error if currentDeploymentName format is not correct', async () => { + await fs.promises.writeFile(filePath, stringify({ ...config, currentDeploymentName: 5 })) + expectFailedValidation() + + await fs.promises.writeFile(filePath, stringify({ ...config, currentDeploymentName: ['foo', 'bar'] })) + expectFailedValidation() + }) +}) \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index c4dc2ef23..ce138453e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,8 +17,8 @@ "dom" ], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ @@ -36,7 +36,8 @@ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ "types": [ "node", - "mocha" + "mocha", + "reflect-metadata" ], /* Specify type package names to be included without being referenced in a source file. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */