Compare commits
38 Commits
master
...
feature-pr
| Author | SHA1 | Date | |
|---|---|---|---|
| eb7f59e19b | |||
| 4eea9bce6f | |||
| 8813e27a90 | |||
| b2d1a6e958 | |||
| 0d3bf40b59 | |||
| 459080d1e6 | |||
| e9216cc3e4 | |||
| cafc857e56 | |||
| e83fcd0b4d | |||
| 4151d0335c | |||
| 0f7ccb2a0a | |||
| 450745011a | |||
| e220b7930c | |||
| b53558e2d1 | |||
| baab2e75d9 | |||
| 4f1847efa8 | |||
| 300a538314 | |||
| e696e5d9d2 | |||
| 75368c8ef8 | |||
| 52e00c3f38 | |||
| 83a0c1cf31 | |||
| f432f7796c | |||
| ebc3f07610 | |||
| 773fcc7ef3 | |||
| e897ca7848 | |||
| 9c09560b73 | |||
| 126c6f4884 | |||
| 9ddda03bd6 | |||
| a8f8bdda99 | |||
| 987e79c592 | |||
| cedbab4989 | |||
| 9ea2787ba2 | |||
| 2eb65f7c55 | |||
| bcdbf8f7c6 | |||
| e925828328 | |||
| 136b3e386c | |||
| 23a242fc16 | |||
| 4fd0118ac1 |
269
bun.lock
269
bun.lock
@ -4,10 +4,17 @@
|
||||
"": {
|
||||
"name": "cli-framework",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.27.0",
|
||||
"@maxmorozoff/try-catch-tuple": "^0.1.2",
|
||||
"@maxmorozoff/try-catch-tuple-ts-plugin": "^0.0.1",
|
||||
"@types/bun": "latest",
|
||||
"dprint": "^0.50.0",
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-plugin-fp": "^2.3.0",
|
||||
"globals": "^16.1.0",
|
||||
"ts-patch": "^3.3.0",
|
||||
"typescript-eslint": "^8.32.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.8.3",
|
||||
@ -15,62 +22,320 @@
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@dprint/darwin-arm64": ["@dprint/darwin-arm64@0.50.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-KqWpsvm4JveYdKDLSLlQINGNW4pEAGHcTFPEHR5qXMYV4pPomLgHHPyBrxe3XdGtlUp4I8HfvBMBw3b/LKd06A=="],
|
||||
|
||||
"@dprint/darwin-x64": ["@dprint/darwin-x64@0.50.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-kFeeLYhCIVAe1SMtFYk1q0qWxrkmW8FhOBTUh2oblr4AnAjpjb03m8BVUrHHKFeBTsppwck+1b8hzU6LRZO7fA=="],
|
||||
|
||||
"@dprint/linux-arm64-glibc": ["@dprint/linux-arm64-glibc@0.50.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-EL0+uMSdj/n+cZOP9ZO8ndvjmtOSWXNsMHKdAAaTG0+EjH9M9YKXD6kopP6PKOR5pJuiyHCRpVKJ4xoD4adfpQ=="],
|
||||
|
||||
"@dprint/linux-arm64-musl": ["@dprint/linux-arm64-musl@0.50.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-bzyYxKtFw/hYAA+7lWQGQGo2YFPnH7Ql9uWxxWqiGaWVPU66K9WQt0RUEqu1hQBrCk9mMz3jx5l4oKWQ/Dc0fw=="],
|
||||
|
||||
"@dprint/linux-riscv64-glibc": ["@dprint/linux-riscv64-glibc@0.50.0", "", { "os": "linux", "cpu": "none" }, "sha512-ElFqmKs96NyVXWqd2SJGJGtyVmUWNiLUyaImEzL7XZRmpoJG+Ky7SryhccMQU0ENtQmY0CVgZipLZ1SqhIoluA=="],
|
||||
|
||||
"@dprint/linux-x64-glibc": ["@dprint/linux-x64-glibc@0.50.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Kim8TtCdpCQUNqF2D96vunuonYy6tPfp/AQblSVA4ADChVyFLGfPaQIECpGAAKxXnIG2SX5JRQP7nB/4JgPNbA=="],
|
||||
|
||||
"@dprint/linux-x64-musl": ["@dprint/linux-x64-musl@0.50.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ChZf0BnS3S6BIfqAPgQKqEh/7vgD1xc0MpcFcTrvkVQHuSdCQu1XiqUN12agzxB+Y5Ml9exgzP8lYgNza7iXvw=="],
|
||||
|
||||
"@dprint/win32-arm64": ["@dprint/win32-arm64@0.50.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-xSY607bRyIPG7UO3uRa5c5wTGHKJqLUkQst85Hcz89EL/It6wswwUSNcywDydssN99HmSHop4fIf6FJTEpEp2g=="],
|
||||
|
||||
"@dprint/win32-x64": ["@dprint/win32-x64@0.50.0", "", { "os": "win32", "cpu": "x64" }, "sha512-uGDjrK88LOet9a8pPRM9nKins93mK2NLozqL/hCNV88Nu5Nk0bBeVwRMAnPapjV3Jo+hsJOeq3Z1ibrq2c3v8w=="],
|
||||
|
||||
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="],
|
||||
|
||||
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
|
||||
|
||||
"@eslint/config-array": ["@eslint/config-array@0.20.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ=="],
|
||||
|
||||
"@eslint/config-helpers": ["@eslint/config-helpers@0.2.2", "", {}, "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg=="],
|
||||
|
||||
"@eslint/core": ["@eslint/core@0.14.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg=="],
|
||||
|
||||
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "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.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
|
||||
|
||||
"@eslint/js": ["@eslint/js@9.27.0", "", {}, "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA=="],
|
||||
|
||||
"@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
|
||||
|
||||
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.1", "", { "dependencies": { "@eslint/core": "^0.14.0", "levn": "^0.4.1" } }, "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w=="],
|
||||
|
||||
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
||||
|
||||
"@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
|
||||
|
||||
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
|
||||
|
||||
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
|
||||
|
||||
"@maxmorozoff/try-catch-tuple": ["@maxmorozoff/try-catch-tuple@0.1.2", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-stFwucLpAkJPkLIlBGsXB/Q9A3kSTFoioqqlz7V5r6pwsysQ1/+vpVKEutmbjSztoIOdy+iTn/iSSKyJXb+ebQ=="],
|
||||
|
||||
"@maxmorozoff/try-catch-tuple-ts-plugin": ["@maxmorozoff/try-catch-tuple-ts-plugin@0.0.1", "", { "peerDependencies": { "ts-patch": "^3.3.0", "typescript": "^5.0.0" }, "optionalPeers": ["ts-patch"] }, "sha512-OeQpI8YfkuB1gFXSCyogsllfoMmEYnZvIRsSV+MWOxk/amrpA4rKjpbmqaHpVVo3YKFLqTLB8RLDFgVkoWBGhQ=="],
|
||||
|
||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||
|
||||
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||
|
||||
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.2.13", "", { "dependencies": { "bun-types": "1.2.13" } }, "sha512-u6vXep/i9VBxoJl3GjZsl/BFIsvML8DfVDO0RYLEwtSZSp981kEO1V5NwRcO1CPJ7AmvpbnDCiMKo3JvbDEjAg=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
|
||||
|
||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||
|
||||
"@types/node": ["@types/node@22.14.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.32.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.32.1", "@typescript-eslint/type-utils": "8.32.1", "@typescript-eslint/utils": "8.32.1", "@typescript-eslint/visitor-keys": "8.32.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg=="],
|
||||
|
||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.32.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.32.1", "@typescript-eslint/types": "8.32.1", "@typescript-eslint/typescript-estree": "8.32.1", "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg=="],
|
||||
|
||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.32.1", "", { "dependencies": { "@typescript-eslint/types": "8.32.1", "@typescript-eslint/visitor-keys": "8.32.1" } }, "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA=="],
|
||||
|
||||
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.32.1", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.32.1", "@typescript-eslint/utils": "8.32.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA=="],
|
||||
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.32.1", "", {}, "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.32.1", "", { "dependencies": { "@typescript-eslint/types": "8.32.1", "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg=="],
|
||||
|
||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.32.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.32.1", "@typescript-eslint/types": "8.32.1", "@typescript-eslint/typescript-estree": "8.32.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA=="],
|
||||
|
||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.32.1", "", { "dependencies": { "@typescript-eslint/types": "8.32.1", "eslint-visitor-keys": "^4.2.0" } }, "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w=="],
|
||||
|
||||
"acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="],
|
||||
|
||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||
|
||||
"ajv": ["ajv@6.12.6", "", { "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" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
||||
|
||||
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||
|
||||
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-rRjA1T6n7wto4gxhAO/ErZEtOXyEZEmnIHQfl0Dt1QQSB4QV0iP6BZ9/YB5fZaHFQ2dwHFrmPaRQ9GGMX01k9Q=="],
|
||||
|
||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||
|
||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||
|
||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||
|
||||
"create-eslint-index": ["create-eslint-index@1.0.0", "", { "dependencies": { "lodash.get": "^4.3.0" } }, "sha512-nXvJjnfDytOOaPOonX0h0a1ggMoqrhdekGeZkD6hkcWYvlCWhU719tKFVh8eU04CnMwu3uwe1JjwuUF2C3k2qg=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
||||
|
||||
"dprint": ["dprint@0.50.0", "", { "optionalDependencies": { "@dprint/darwin-arm64": "0.50.0", "@dprint/darwin-x64": "0.50.0", "@dprint/linux-arm64-glibc": "0.50.0", "@dprint/linux-arm64-musl": "0.50.0", "@dprint/linux-riscv64-glibc": "0.50.0", "@dprint/linux-x64-glibc": "0.50.0", "@dprint/linux-x64-musl": "0.50.0", "@dprint/win32-arm64": "0.50.0", "@dprint/win32-x64": "0.50.0" }, "bin": { "dprint": "bin.js" } }, "sha512-aNJhOQsUS5D9k/YkMUaLLniIpxEBUR0ZwT0RXGQV5YpaGwE2nx6FcKuVkC6wRaZXTr8X0NpV/2HFbcvNuI2jtA=="],
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||
|
||||
"eslint": ["eslint@9.27.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", "@eslint/config-helpers": "^0.2.1", "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.27.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q=="],
|
||||
|
||||
"eslint-ast-utils": ["eslint-ast-utils@1.1.0", "", { "dependencies": { "lodash.get": "^4.4.2", "lodash.zip": "^4.2.0" } }, "sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA=="],
|
||||
|
||||
"eslint-config-prettier": ["eslint-config-prettier@10.1.5", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw=="],
|
||||
|
||||
"eslint-plugin-fp": ["eslint-plugin-fp@2.3.0", "", { "dependencies": { "create-eslint-index": "^1.0.0", "eslint-ast-utils": "^1.0.0", "lodash": "^4.13.1", "req-all": "^0.1.0" }, "peerDependencies": { "eslint": ">=3" } }, "sha512-3n2oHibwoIxAht9/+ZaTldhI6brXORgl8oNXqZd+d9xuAQt2SBJ2/aml0oQRMWvXrgsz2WG6wfC++NjzSG3prA=="],
|
||||
|
||||
"eslint-scope": ["eslint-scope@8.3.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ=="],
|
||||
|
||||
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="],
|
||||
|
||||
"espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="],
|
||||
|
||||
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
|
||||
|
||||
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
|
||||
|
||||
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
|
||||
|
||||
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
||||
|
||||
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
||||
|
||||
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
|
||||
|
||||
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
|
||||
|
||||
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
||||
|
||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||
|
||||
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
|
||||
|
||||
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
|
||||
|
||||
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||
|
||||
"global-prefix": ["global-prefix@4.0.0", "", { "dependencies": { "ini": "^4.1.3", "kind-of": "^6.0.3", "which": "^4.0.0" } }, "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA=="],
|
||||
|
||||
"globals": ["globals@16.1.0", "", {}, "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g=="],
|
||||
|
||||
"graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
|
||||
|
||||
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||
|
||||
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
||||
|
||||
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
||||
|
||||
"ini": ["ini@4.1.3", "", {}, "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg=="],
|
||||
|
||||
"is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
|
||||
|
||||
"isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
||||
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||
|
||||
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
||||
|
||||
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
||||
|
||||
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
|
||||
|
||||
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
||||
|
||||
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
||||
|
||||
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||
|
||||
"kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="],
|
||||
|
||||
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
||||
|
||||
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||
|
||||
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||
|
||||
"lodash.get": ["lodash.get@4.4.2", "", {}, "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="],
|
||||
|
||||
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
||||
|
||||
"lodash.zip": ["lodash.zip@4.2.0", "", {}, "sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg=="],
|
||||
|
||||
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||
|
||||
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
||||
|
||||
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||
|
||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
||||
|
||||
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
|
||||
|
||||
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
||||
|
||||
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
||||
|
||||
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
||||
|
||||
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
||||
|
||||
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||
|
||||
"req-all": ["req-all@0.1.0", "", {}, "sha512-ZdvPr8uXy9ujX3KujwE2P1HWkMYgogIhqeAeyb47MqWjSfyxERSm0TNbN/IapCCmWDufXab04AYrRgObaJCJ6Q=="],
|
||||
|
||||
"resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
|
||||
|
||||
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||
|
||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||
|
||||
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||
|
||||
"semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
||||
|
||||
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||
|
||||
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
||||
|
||||
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||
|
||||
"ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
|
||||
|
||||
"ts-patch": ["ts-patch@3.3.0", "", { "dependencies": { "chalk": "^4.1.2", "global-prefix": "^4.0.0", "minimist": "^1.2.8", "resolve": "^1.22.2", "semver": "^7.6.3", "strip-ansi": "^6.0.1" }, "bin": { "ts-patch": "bin/ts-patch.js", "tspc": "bin/tspc.js" } }, "sha512-zAOzDnd5qsfEnjd9IGy1IRuvA7ygyyxxdxesbhMdutt8AHFjD8Vw8hU2rMF89HX1BKRWFYqKHrO8Q6lw0NeUZg=="],
|
||||
|
||||
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
||||
|
||||
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
||||
|
||||
"typescript-eslint": ["typescript-eslint@8.32.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.32.1", "@typescript-eslint/parser": "8.32.1", "@typescript-eslint/utils": "8.32.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg=="],
|
||||
|
||||
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||
|
||||
"which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="],
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
||||
|
||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||
|
||||
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||
|
||||
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
|
||||
|
||||
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.4", "", {}, "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"global-prefix/which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
||||
"global-prefix/which/isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
||||
}
|
||||
}
|
||||
|
||||
1
cli-tool-drawboard.tldr
Normal file
1
cli-tool-drawboard.tldr
Normal file
File diff suppressed because one or more lines are too long
56
dprint.json
Normal file
56
dprint.json
Normal file
@ -0,0 +1,56 @@
|
||||
{
|
||||
"typescript": {
|
||||
"lineWidth": 80,
|
||||
"indentWidth": 2,
|
||||
"useTabs": false,
|
||||
"semiColons": "asi",
|
||||
"quoteStyle": "alwaysSingle",
|
||||
"trailingCommas": "onlyMultiLine",
|
||||
"operatorPosition": "nextLine",
|
||||
"arrowFunction.useParentheses": "maintain",
|
||||
"binaryExpression.linePerExpression": false,
|
||||
"memberExpression.linePerExpression": false,
|
||||
"bracePosition": "sameLineUnlessHanging",
|
||||
"spaceSurroundingProperties": false,
|
||||
"objectExpression.spaceSurroundingProperties": false,
|
||||
"objectPattern.spaceSurroundingProperties": false,
|
||||
"typeLiteral.spaceSurroundingProperties": false,
|
||||
"functionDeclaration.spaceBeforeParentheses": false,
|
||||
"method.spaceBeforeParentheses": false,
|
||||
"constructor.spaceBeforeParentheses": false,
|
||||
"typeAnnotation.spaceBeforeColon": false,
|
||||
"jsx.quoteStyle": "preferSingle",
|
||||
"jsxSelfClosingElement.spaceBeforeSlash": false,
|
||||
"useBraces": "whenNotSingleLine",
|
||||
"preferHanging": false,
|
||||
"conditionalExpression.linePerExpression": false,
|
||||
|
||||
"arguments.preferHanging": "never",
|
||||
"arguments.spaceAround": false,
|
||||
|
||||
"parameters.spaceAround": false,
|
||||
"parameters.trailingCommas": "onlyMultiLine",
|
||||
"parameters.preferSingleLine": true,
|
||||
"parameters.preferHanging": "onlySingleItem",
|
||||
|
||||
"typeLiteral.separatorKind": "comma",
|
||||
|
||||
"parentheses.preferSingleLine": true
|
||||
},
|
||||
"json": {},
|
||||
"markdown": {},
|
||||
"toml": {},
|
||||
"malva": {},
|
||||
"markup": {},
|
||||
"yaml": {},
|
||||
"excludes": ["**/node_modules", "**/*-lock.json"],
|
||||
"plugins": [
|
||||
"https://plugins.dprint.dev/typescript-0.95.4.wasm",
|
||||
"https://plugins.dprint.dev/json-0.20.0.wasm",
|
||||
"https://plugins.dprint.dev/markdown-0.18.0.wasm",
|
||||
"https://plugins.dprint.dev/toml-0.7.0.wasm",
|
||||
"https://plugins.dprint.dev/g-plane/malva-v0.12.1.wasm",
|
||||
"https://plugins.dprint.dev/g-plane/markup_fmt-v0.20.0.wasm",
|
||||
"https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.1.wasm"
|
||||
]
|
||||
}
|
||||
23
eslint.config.js
Normal file
23
eslint.config.js
Normal file
@ -0,0 +1,23 @@
|
||||
import fp from 'eslint-plugin-fp';
|
||||
import js from '@eslint/js';
|
||||
import globals from 'globals';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import { defineConfig } from 'eslint/config';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier/flat';
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
files: ['**/*.{js,mjs,cjs,ts}'],
|
||||
plugins: { js, fp },
|
||||
extends: ['js/recommended'],
|
||||
rules: {
|
||||
'fp/no-mutation': 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.{js,mjs,cjs,ts}'],
|
||||
languageOptions: { globals: globals.browser },
|
||||
},
|
||||
tseslint.configs.recommended,
|
||||
eslintConfigPrettier,
|
||||
]);
|
||||
@ -4,10 +4,17 @@
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.27.0",
|
||||
"@maxmorozoff/try-catch-tuple": "^0.1.2",
|
||||
"@maxmorozoff/try-catch-tuple-ts-plugin": "^0.0.1",
|
||||
"@types/bun": "latest",
|
||||
"ts-patch": "^3.3.0"
|
||||
"dprint": "^0.50.0",
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-plugin-fp": "^2.3.0",
|
||||
"globals": "^16.1.0",
|
||||
"ts-patch": "^3.3.0",
|
||||
"typescript-eslint": "^8.32.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.8.3"
|
||||
|
||||
@ -1,88 +1,92 @@
|
||||
import { type ParseArgsOptionsConfig } from 'util'
|
||||
import type { BrigadierTreeCommand, BrigadierTree } from "./types";
|
||||
import { type ParseArgsOptionsConfig } from 'util';
|
||||
import type { BrigadierTreeCommand, BrigadierTree } from './types';
|
||||
|
||||
const commandObj:
|
||||
{ _opts?: ParseArgsOptionsConfig } &
|
||||
Record<string, BrigadierTreeCommand> = {}
|
||||
const commandObj: { _opts?: ParseArgsOptionsConfig } & Record<
|
||||
string,
|
||||
BrigadierTreeCommand
|
||||
> = {};
|
||||
|
||||
commandObj._opts = {
|
||||
verbose: {
|
||||
type: "boolean",
|
||||
type: 'boolean',
|
||||
short: 'v',
|
||||
},
|
||||
silent: {
|
||||
type: "boolean",
|
||||
short: "s",
|
||||
type: 'boolean',
|
||||
short: 's',
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
commandObj.run = {
|
||||
command: "run",
|
||||
command: 'run',
|
||||
options: {
|
||||
speed: {
|
||||
type: "string",
|
||||
}
|
||||
}
|
||||
}
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
commandObj.walk = {
|
||||
command: "walk",
|
||||
command: 'walk',
|
||||
options: {
|
||||
fancy: {
|
||||
type: "boolean",
|
||||
short: "f"
|
||||
}
|
||||
}
|
||||
}
|
||||
type: 'boolean',
|
||||
short: 'f',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
commandObj.stand = {
|
||||
command: "stand",
|
||||
}
|
||||
command: 'stand',
|
||||
};
|
||||
|
||||
commandObj.sit = {
|
||||
command: "sit",
|
||||
}
|
||||
command: 'sit',
|
||||
};
|
||||
|
||||
commandObj.say = {
|
||||
command: "say",
|
||||
command: 'say',
|
||||
value: {
|
||||
required: true,
|
||||
inputType: "string",
|
||||
dataType: "string[]",
|
||||
inputType: 'string',
|
||||
dataType: 'string[]',
|
||||
},
|
||||
options: {
|
||||
accent: {
|
||||
type: 'string',
|
||||
default: "American",
|
||||
}
|
||||
default: 'American',
|
||||
},
|
||||
},
|
||||
subcommands: [
|
||||
{
|
||||
command: "talk",
|
||||
command: 'talk',
|
||||
value: {
|
||||
required: true,
|
||||
inputType: "string",
|
||||
dataType: "string[]"
|
||||
inputType: 'string',
|
||||
dataType: 'string[]',
|
||||
},
|
||||
},
|
||||
{
|
||||
command: "whisper",
|
||||
command: 'whisper',
|
||||
value: {
|
||||
required: true,
|
||||
inputType: "string",
|
||||
dataType: "string[]"
|
||||
}
|
||||
inputType: 'string',
|
||||
dataType: 'string[]',
|
||||
},
|
||||
},
|
||||
{
|
||||
command: "yell",
|
||||
command: 'yell',
|
||||
value: {
|
||||
required: true,
|
||||
inputType: "string",
|
||||
dataType: "string[]"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
inputType: 'string',
|
||||
dataType: 'string[]',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const { _opts, ...commands } = commandObj
|
||||
export const main: BrigadierTree = { _opts, commands: [...Object.values(commands)] }
|
||||
const { _opts, ...commands } = commandObj;
|
||||
export const main: BrigadierTree = {
|
||||
_opts,
|
||||
commands: [...Object.values(commands)],
|
||||
};
|
||||
|
||||
@ -1,24 +1,24 @@
|
||||
export class BrigadierInternalError extends Error {
|
||||
constructor(error: Error | undefined = undefined) {
|
||||
super()
|
||||
super();
|
||||
|
||||
this.name = this.constructor.name;
|
||||
this.message = "The argument parser experience an internal error."
|
||||
if (error) this.cause = error
|
||||
this.message = 'The argument parser experience an internal error.';
|
||||
if (error) this.cause = error;
|
||||
}
|
||||
}
|
||||
|
||||
export class BrigadierConfigError extends Error {
|
||||
constructor(message: string, error: Error | undefined = undefined) {
|
||||
super(message)
|
||||
super(message);
|
||||
|
||||
this.name = this.constructor.name;
|
||||
if (error) this.cause = error
|
||||
if (error) this.cause = error;
|
||||
}
|
||||
}
|
||||
|
||||
export class BrigadierOptionError extends Error {
|
||||
#defaultCode = "ERR_PARSE_ARGS_BRIGADIER_PARSE_OPTION";
|
||||
#defaultCode = 'ERR_PARSE_ARGS_BRIGADIER_PARSE_OPTION';
|
||||
#defaultMessages = {
|
||||
helpPrimitive: `Run '--help' for more information on how to use this command.`,
|
||||
get generic() {
|
||||
@ -40,7 +40,7 @@ export class BrigadierOptionError extends Error {
|
||||
constructor(message: string, error: Error) {
|
||||
super(message);
|
||||
|
||||
if ("code" in error && typeof error.code === "string")
|
||||
if ('code' in error && typeof error.code === 'string')
|
||||
this.#switchOnCodes(error.code);
|
||||
|
||||
this.name = this.constructor.name;
|
||||
@ -48,12 +48,12 @@ export class BrigadierOptionError extends Error {
|
||||
}
|
||||
|
||||
#switchOnCodes(code: string) {
|
||||
if (code === "ERR_PARSE_ARGS_UNKNOWN_OPTION") {
|
||||
if (code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION') {
|
||||
this.message = this.#unknownOption(this.message);
|
||||
this.code = code;
|
||||
return;
|
||||
}
|
||||
if (code === "ERR_PARSE_ARGS_INVALID_OPTION_VALUE") {
|
||||
if (code === 'ERR_PARSE_ARGS_INVALID_OPTION_VALUE') {
|
||||
this.message = this.#invalidOptionValue(this.message);
|
||||
this.code = code;
|
||||
return;
|
||||
@ -62,15 +62,15 @@ export class BrigadierOptionError extends Error {
|
||||
|
||||
#unknownOption(message: string) {
|
||||
const index = 18;
|
||||
if (typeof message !== "string" || message[index - 3] !== "'")
|
||||
if (typeof message !== 'string' || message[index - 3] !== "'")
|
||||
return this.#defaultMessages.unknownOption;
|
||||
this.option = this.#extractOptionString(message, index, ". ");
|
||||
this.option = this.#extractOptionString(message, index, '. ');
|
||||
return `Unknown option '--${this.option}'. ${this.#defaultMessages.helpPrimitive}`;
|
||||
}
|
||||
|
||||
#invalidOptionValue(message: string) {
|
||||
const index = 10;
|
||||
if (typeof message !== "string" || message[index - 3] !== "'")
|
||||
if (typeof message !== 'string' || message[index - 3] !== "'")
|
||||
return this.#defaultMessages.invalidOptionValue;
|
||||
this.option = this.#extractOptionString(message, index, "<value>' ");
|
||||
return `Option '--${this.option}' has an invalid value. ${this.#defaultMessages.helpPrimitive}`;
|
||||
|
||||
@ -1,13 +1,22 @@
|
||||
import { tryCatch } from "@maxmorozoff/try-catch-tuple";
|
||||
import { tryCatch } from '@maxmorozoff/try-catch-tuple';
|
||||
import {
|
||||
parseArgs,
|
||||
type ParseArgsConfig,
|
||||
type ParseArgsOptionsConfig,
|
||||
} from "util";
|
||||
import { assertArrayOfStrings } from "../util/typeCheck";
|
||||
import { BrigadierConfigError, BrigadierInternalError, BrigadierOptionError } from "./errors";
|
||||
import type { BrigadierTree, BrigadierParserOverrides, BrigadierTreeCommand, BrigadierOutput } from "./types";
|
||||
import { treeUtils } from "./tree";
|
||||
} from 'util';
|
||||
import { assertArrayOfStrings } from '../util/typeCheck';
|
||||
import {
|
||||
BrigadierConfigError,
|
||||
BrigadierInternalError,
|
||||
BrigadierOptionError,
|
||||
} from './errors';
|
||||
import type {
|
||||
BrigadierTree,
|
||||
BrigadierParserOverrides,
|
||||
BrigadierTreeCommand,
|
||||
BrigadierOutput,
|
||||
} from './types';
|
||||
import { treeUtils } from './tree';
|
||||
|
||||
const defaultOverrides: BrigadierParserOverrides = {
|
||||
allowPositionals: true,
|
||||
@ -17,45 +26,55 @@ const defaultOverrides: BrigadierParserOverrides = {
|
||||
};
|
||||
|
||||
function findNextTree(tree: Array<BrigadierTreeCommand>, command: string) {
|
||||
const nextCommand = tree.filter((obj) => obj.command === command)
|
||||
return nextCommand[0]?.subcommands
|
||||
const nextCommand = tree.filter((obj) => obj.command === command);
|
||||
return nextCommand[0]?.subcommands;
|
||||
}
|
||||
|
||||
function findCommands(tree: Array<BrigadierTreeCommand> | undefined, args: Array<string>, out: Array<string> = []) {
|
||||
const item = args[0]
|
||||
if (!item || !tree) return out
|
||||
function findCommands(
|
||||
tree: Array<BrigadierTreeCommand> | undefined,
|
||||
args: Array<string>,
|
||||
out: Array<string> = []
|
||||
) {
|
||||
const item = args[0];
|
||||
if (!item || !tree) return out;
|
||||
|
||||
const [commands, commandError] = tryCatch(() => treeUtils.commandArray(tree))
|
||||
if (commandError) throw commandError
|
||||
const [commands, commandError] = tryCatch(() => treeUtils.commandArray(tree));
|
||||
if (commandError) throw commandError;
|
||||
|
||||
if (item.slice(0, 0) === "-" || !commands.includes(item)) return out
|
||||
if (item.slice(0, 0) === '-' || !commands.includes(item)) return out;
|
||||
|
||||
const [newTree, newTreeError] = tryCatch(() => findNextTree(tree, item))
|
||||
if (newTreeError) throw newTreeError
|
||||
const [newTree, newTreeError] = tryCatch(() => findNextTree(tree, item));
|
||||
if (newTreeError) throw newTreeError;
|
||||
|
||||
const newArgs = args.slice(1)
|
||||
const newOut = [...out, item]
|
||||
const newArgs = args.slice(1);
|
||||
const newOut = [...out, item];
|
||||
|
||||
return findCommands(newTree, newArgs, newOut)
|
||||
return findCommands(newTree, newArgs, newOut);
|
||||
}
|
||||
|
||||
function findOpts(tree: Array<BrigadierTreeCommand>, commands: Array<string>, opts: ParseArgsOptionsConfig = {}) {
|
||||
const item = commands[0]
|
||||
if (!item) throw new TypeError("The first item in the array is missing.")
|
||||
function findOpts(
|
||||
tree: Array<BrigadierTreeCommand>,
|
||||
commands: Array<string>,
|
||||
opts: ParseArgsOptionsConfig = {}
|
||||
) {
|
||||
const item = commands[0];
|
||||
if (!item) throw new TypeError('The first item in the array is missing.');
|
||||
|
||||
const commandObj = tree.filter((obj) => obj.command === item)
|
||||
const thisOpts = commandObj[0]?.options ? commandObj[0].options : {}
|
||||
const nextOpts = Object.assign(opts, thisOpts)
|
||||
const commandObj = tree.filter((obj) => obj.command === item);
|
||||
const thisOpts = commandObj[0]?.options ? commandObj[0].options : {};
|
||||
const nextOpts = Object.assign(opts, thisOpts);
|
||||
|
||||
if (commands.length === 1) {
|
||||
const config = nextOpts
|
||||
return config
|
||||
const config = nextOpts;
|
||||
return config;
|
||||
}
|
||||
|
||||
const nextTree = commandObj[0]?.subcommands
|
||||
if (!nextTree) throw new BrigadierConfigError(`The '${item}' command was expected to have subcommands.`)
|
||||
return findOpts(nextTree, commands.slice(1), nextOpts)
|
||||
|
||||
const nextTree = commandObj[0]?.subcommands;
|
||||
if (!nextTree)
|
||||
throw new BrigadierConfigError(
|
||||
`The '${item}' command was expected to have subcommands.`
|
||||
);
|
||||
return findOpts(nextTree, commands.slice(1), nextOpts);
|
||||
}
|
||||
|
||||
function setParsedArgsConfig(
|
||||
@ -79,21 +98,32 @@ function setParsedArgsConfig(
|
||||
return { config: configObj, commands: commands };
|
||||
}
|
||||
|
||||
function generateParsedArgsConfig(tree: BrigadierTree, args: Array<string>, overrides: BrigadierParserOverrides | undefined = undefined) {
|
||||
const [commandsArray, commandsError] = tryCatch(() => findCommands(tree.commands, args.slice(2)))
|
||||
if (commandsError) throw commandsError
|
||||
function generateParsedArgsConfig(
|
||||
tree: BrigadierTree,
|
||||
args: Array<string>,
|
||||
overrides: BrigadierParserOverrides | undefined = undefined
|
||||
) {
|
||||
const [commandsArray, commandsError] = tryCatch(() =>
|
||||
findCommands(tree.commands, args.slice(2))
|
||||
);
|
||||
if (commandsError) throw commandsError;
|
||||
if (!commandsArray[0]) {
|
||||
const opts = tree._opts
|
||||
return setParsedArgsConfig(args, opts, overrides, commandsArray)
|
||||
const opts = tree._opts;
|
||||
return setParsedArgsConfig(args, opts, overrides, commandsArray);
|
||||
}
|
||||
|
||||
const [commandOpts, commandOptsError] = tryCatch(() => findOpts(tree.commands, commandsArray))
|
||||
if (commandOptsError) throw commandOptsError
|
||||
const opts = Object.assign({}, tree._opts, commandOpts)
|
||||
return setParsedArgsConfig(args, opts, overrides, commandsArray)
|
||||
const [commandOpts, commandOptsError] = tryCatch(() =>
|
||||
findOpts(tree.commands, commandsArray)
|
||||
);
|
||||
if (commandOptsError) throw commandOptsError;
|
||||
const opts = Object.assign({}, tree._opts, commandOpts);
|
||||
return setParsedArgsConfig(args, opts, overrides, commandsArray);
|
||||
}
|
||||
|
||||
function getParsedArgs(opts: { config: ParseArgsConfig, commands: Array<string> }) {
|
||||
function getParsedArgs(opts: {
|
||||
config: ParseArgsConfig;
|
||||
commands: Array<string>;
|
||||
}) {
|
||||
const [result, error] = tryCatch(() => {
|
||||
return parseArgs(opts.config);
|
||||
});
|
||||
@ -101,39 +131,63 @@ function getParsedArgs(opts: { config: ParseArgsConfig, commands: Array<string>
|
||||
return { ...result, commands: opts.commands };
|
||||
}
|
||||
|
||||
function buildPositionalsArray(commands: Array<string>, positionals: Array<string>) {
|
||||
const testSet = new Set(commands)
|
||||
return positionals.filter(str => !testSet.has(str))
|
||||
function buildPositionalsArray(
|
||||
commands: Array<string>,
|
||||
positionals: Array<string>
|
||||
) {
|
||||
const testSet = new Set(commands);
|
||||
return positionals.filter((str) => !testSet.has(str));
|
||||
}
|
||||
|
||||
function handleErr(error: Error) {
|
||||
if (error instanceof BrigadierOptionError || error instanceof BrigadierConfigError) return error
|
||||
return new BrigadierInternalError()
|
||||
if (
|
||||
error instanceof BrigadierOptionError ||
|
||||
error instanceof BrigadierConfigError
|
||||
)
|
||||
return error;
|
||||
return new BrigadierInternalError();
|
||||
}
|
||||
|
||||
function buildOutputObject(tree: BrigadierTree, args: Array<string>, overrides: BrigadierParserOverrides | undefined = undefined) {
|
||||
function buildOutputObject(
|
||||
tree: BrigadierTree,
|
||||
args: Array<string>,
|
||||
overrides: BrigadierParserOverrides | undefined = undefined
|
||||
) {
|
||||
try {
|
||||
const [config, configError] = tryCatch(() => generateParsedArgsConfig(tree, args, overrides))
|
||||
if (configError) throw configError
|
||||
const [config, configError] = tryCatch(() =>
|
||||
generateParsedArgsConfig(tree, args, overrides)
|
||||
);
|
||||
if (configError) throw configError;
|
||||
|
||||
const [parsedArgs, parsedError] = tryCatch(() => getParsedArgs(config))
|
||||
if (parsedError) throw parsedError
|
||||
const [parsedArgs, parsedError] = tryCatch(() => getParsedArgs(config));
|
||||
if (parsedError) throw parsedError;
|
||||
|
||||
const programPaths = {
|
||||
bun: parsedArgs.positionals[0] ? parsedArgs.positionals[0] : "",
|
||||
path: parsedArgs.positionals[1] ? parsedArgs.positionals[1] : "",
|
||||
}
|
||||
const command = config.commands[0] ? { command: config.commands[0] } : {}
|
||||
const subcommands = config.commands[1] ? { subcommands: config.commands.slice(1) } : {}
|
||||
const [positionals, positionalsError] = tryCatch(() => buildPositionalsArray(config.commands, parsedArgs.positionals.slice(2)))
|
||||
if (positionalsError) throw positionalsError
|
||||
const positionalsObj = positionals.length > 0 ? { positionals } : {}
|
||||
const values = parsedArgs.values ? parsedArgs.values : {}
|
||||
bun: parsedArgs.positionals[0] ? parsedArgs.positionals[0] : '',
|
||||
path: parsedArgs.positionals[1] ? parsedArgs.positionals[1] : '',
|
||||
};
|
||||
const command = config.commands[0] ? { command: config.commands[0] } : {};
|
||||
const subcommands = config.commands[1]
|
||||
? { subcommands: config.commands.slice(1) }
|
||||
: {};
|
||||
const [positionals, positionalsError] = tryCatch(() =>
|
||||
buildPositionalsArray(config.commands, parsedArgs.positionals.slice(2))
|
||||
);
|
||||
if (positionalsError) throw positionalsError;
|
||||
const positionalsObj = positionals.length > 0 ? { positionals } : {};
|
||||
const values = parsedArgs.values ? parsedArgs.values : {};
|
||||
|
||||
const out: BrigadierOutput = Object.assign({}, programPaths, command, subcommands, positionalsObj, { values })
|
||||
return out
|
||||
const out: BrigadierOutput = Object.assign(
|
||||
{},
|
||||
programPaths,
|
||||
command,
|
||||
subcommands,
|
||||
positionalsObj,
|
||||
{ values }
|
||||
);
|
||||
return out;
|
||||
} catch (error) {
|
||||
throw handleErr(error as Error)
|
||||
throw handleErr(error as Error);
|
||||
}
|
||||
}
|
||||
export const main = buildOutputObject;
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
import { demoTree } from ".";
|
||||
import type { BrigadierTreeCommand } from "./types";
|
||||
|
||||
import { demoTree } from '.';
|
||||
import type { BrigadierTreeCommand } from './types';
|
||||
|
||||
function commandArray(array: Array<BrigadierTreeCommand>) {
|
||||
const out: Array<string> = []
|
||||
array.forEach((obj) => out.push(obj.command))
|
||||
return out
|
||||
const out: Array<string> = [];
|
||||
array.forEach((obj) => out.push(obj.command));
|
||||
return out;
|
||||
}
|
||||
|
||||
export const treeUtils = {
|
||||
commandArray
|
||||
}
|
||||
commandArray,
|
||||
};
|
||||
|
||||
@ -1,39 +1,39 @@
|
||||
import { type ParseArgsOptionsConfig } from 'util'
|
||||
import { type ParseArgsOptionsConfig } from 'util';
|
||||
|
||||
export type BrigadierCommandValue =
|
||||
"string" |
|
||||
"number" |
|
||||
"boolean" |
|
||||
"string[]" |
|
||||
"number[]" |
|
||||
"boolean[]" |
|
||||
"mixed[]"
|
||||
| 'string'
|
||||
| 'number'
|
||||
| 'boolean'
|
||||
| 'string[]'
|
||||
| 'number[]'
|
||||
| 'boolean[]'
|
||||
| 'mixed[]';
|
||||
|
||||
export type BrigadierTreeCommand = {
|
||||
command: string,
|
||||
command: string;
|
||||
value?: {
|
||||
required: boolean,
|
||||
inputType: "string" | "boolean",
|
||||
dataType: BrigadierCommandValue,
|
||||
default?: string | boolean
|
||||
}
|
||||
options?: ParseArgsOptionsConfig,
|
||||
subcommands?: Array<BrigadierTreeCommand>
|
||||
}
|
||||
required: boolean;
|
||||
inputType: 'string' | 'boolean';
|
||||
dataType: BrigadierCommandValue;
|
||||
default?: string | boolean;
|
||||
};
|
||||
options?: ParseArgsOptionsConfig;
|
||||
subcommands?: Array<BrigadierTreeCommand>;
|
||||
};
|
||||
|
||||
export type BrigadierTree = {
|
||||
_opts: ParseArgsOptionsConfig,
|
||||
commands: Array<BrigadierTreeCommand>
|
||||
}
|
||||
_opts: ParseArgsOptionsConfig;
|
||||
commands: Array<BrigadierTreeCommand>;
|
||||
};
|
||||
|
||||
export type BrigadierOutput = {
|
||||
bun: string,
|
||||
path: string,
|
||||
command?: string,
|
||||
subcommands?: Array<string>,
|
||||
positionals?: Array<string>,
|
||||
values?: Record<string, string | boolean>
|
||||
}
|
||||
bun: string;
|
||||
path: string;
|
||||
command?: string;
|
||||
subcommands?: Array<string>;
|
||||
positionals?: Array<string>;
|
||||
values?: Record<string, string | boolean>;
|
||||
};
|
||||
|
||||
export type BrigadierParserOverrides = {
|
||||
strict?: boolean;
|
||||
@ -43,7 +43,6 @@ export type BrigadierParserOverrides = {
|
||||
};
|
||||
|
||||
export type BrigadierInput = BrigadierTree & {
|
||||
args: Array<string>
|
||||
args: Array<string>;
|
||||
overrides?: BrigadierParserOverrides;
|
||||
};
|
||||
|
||||
|
||||
125
src/lib/painter/canvas.old.ts
Normal file
125
src/lib/painter/canvas.old.ts
Normal file
@ -0,0 +1,125 @@
|
||||
import { ANSI_BUFFERS, ANSI_DYNAMIC, writeAnsi } from '../util/ansi';
|
||||
import { tryCatch } from '@maxmorozoff/try-catch-tuple';
|
||||
|
||||
type CanvasAPI = {
|
||||
clear: () => Promise<void>;
|
||||
moveToOrigin: () => Promise<void>;
|
||||
availableHeight: number;
|
||||
availableWidth: number;
|
||||
};
|
||||
|
||||
type CleanupHandler = (api: {
|
||||
clear: () => Promise<void>;
|
||||
endPosition: { x: number; y: number };
|
||||
}) => Promise<void>;
|
||||
|
||||
export function createCanvas(
|
||||
paint: (api: CanvasAPI) => Promise<void>,
|
||||
cleanup?: CleanupHandler
|
||||
) {
|
||||
let isActive = true;
|
||||
let origin = { x: 0, y: 0 };
|
||||
let currentEnd = { x: 0, y: 0 };
|
||||
|
||||
const canvasAPI: CanvasAPI = {
|
||||
clear: async () => {
|
||||
await writeAnsi([
|
||||
ANSI_BUFFERS.SAVE_CURSOR,
|
||||
ANSI_DYNAMIC.CURSOR_TO(origin.x, origin.y),
|
||||
]);
|
||||
|
||||
// Clear each line in the canvas area
|
||||
for (let line = 0; line <= currentEnd.y - origin.y; line++) {
|
||||
await writeAnsi([
|
||||
ANSI_BUFFERS.CLEAR_LINE,
|
||||
...(line < currentEnd.y - origin.y
|
||||
? [ANSI_DYNAMIC.CURSOR_DOWN(1)]
|
||||
: []),
|
||||
]);
|
||||
}
|
||||
|
||||
await writeAnsi([ANSI_BUFFERS.RESTORE_CURSOR]);
|
||||
},
|
||||
|
||||
moveToOrigin: async () => {
|
||||
await writeAnsi([ANSI_DYNAMIC.CURSOR_TO(origin.x, origin.y)]);
|
||||
},
|
||||
|
||||
get availableHeight() {
|
||||
return currentEnd.y - origin.y;
|
||||
},
|
||||
|
||||
get availableWidth() {
|
||||
return currentEnd.x - origin.x;
|
||||
},
|
||||
};
|
||||
|
||||
async function trackPaintArea(fn: () => Promise<void>) {
|
||||
// Save initial cursor position
|
||||
const [_, saveError] = await writeAnsi([ANSI_BUFFERS.SAVE_CURSOR]);
|
||||
if (saveError) throw saveError;
|
||||
|
||||
const [initialPos, initialPosError] = await tryCatch(getCursorPosition);
|
||||
if (initialPosError) {
|
||||
await writeAnsi([ANSI_BUFFERS.RESTORE_CURSOR]);
|
||||
throw initialPosError;
|
||||
}
|
||||
if (initialPos) origin = initialPos;
|
||||
|
||||
// Execute painting
|
||||
await fn();
|
||||
|
||||
// Record new end position
|
||||
const [endPos, endPosError] = await tryCatch(getCursorPosition);
|
||||
if (endPosError) {
|
||||
await writeAnsi([ANSI_BUFFERS.RESTORE_CURSOR]);
|
||||
throw endPosError;
|
||||
}
|
||||
if (endPos) currentEnd = endPos;
|
||||
|
||||
// Restore original cursor
|
||||
await writeAnsi([ANSI_BUFFERS.RESTORE_CURSOR]);
|
||||
}
|
||||
|
||||
return {
|
||||
async paint() {
|
||||
if (!isActive) return;
|
||||
await trackPaintArea(async () => {
|
||||
await canvasAPI.moveToOrigin();
|
||||
await paint(canvasAPI);
|
||||
});
|
||||
},
|
||||
|
||||
async destroy() {
|
||||
if (!isActive) return;
|
||||
isActive = false;
|
||||
|
||||
if (cleanup) {
|
||||
await cleanup({
|
||||
clear: canvasAPI.clear,
|
||||
endPosition: currentEnd,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function getCursorPosition(): Promise<{ x: number; y: number }> {
|
||||
const [pos, posError] = await tryCatch(async () => {
|
||||
await Bun.write(Bun.stdout, ANSI_BUFFERS.GET_CURSOR_POSITION);
|
||||
|
||||
let response = '';
|
||||
const decoder = new TextDecoder();
|
||||
for await (const chunk of Bun.stdin.stream()) {
|
||||
response += decoder.decode(chunk);
|
||||
if (response.includes('R')) break;
|
||||
}
|
||||
|
||||
const [_, y, x] = response.match(/\[(\d+);(\d+)R/) || [];
|
||||
if (!x) throw TypeError("The 'x' position was unset.");
|
||||
if (!y) throw TypeError("The 'y' position was unset.");
|
||||
return { x: parseInt(x), y: parseInt(y) };
|
||||
});
|
||||
|
||||
return pos || { x: 0, y: 0 };
|
||||
}
|
||||
59
src/lib/painter/canvas.ts
Normal file
59
src/lib/painter/canvas.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { type CursorPos } from 'node:readline'
|
||||
import { type Either, left, right } from '../util/basic/either'
|
||||
import { hasTag, type Tag } from '../util/basic/utility'
|
||||
import { PaintCursorError } from './errors'
|
||||
|
||||
export interface CursorPosAxis extends Tag<'X' | 'Y'> {
|
||||
readonly value: number
|
||||
}
|
||||
|
||||
type CursorPosVal = (tag: 'X' | 'Y') => (x: number) => CursorPosAxis
|
||||
export const cursorPosVal: CursorPosVal = tag => x => ({_tag: tag, value: x})
|
||||
|
||||
export const cursorPos = (x: number) => (y: number): CursorPos => ({
|
||||
rows: x,
|
||||
cols: y,
|
||||
})
|
||||
|
||||
type SafeCursorPos =
|
||||
(x: CursorPosAxis) => (y: CursorPosAxis) => Either<CursorPos, Error>
|
||||
export const safeCursorPos: SafeCursorPos = (x) => y => {
|
||||
return hasTag('X')(x) && hasTag('Y')(y)
|
||||
? right(cursorPos(x.value)(y.value))
|
||||
: hasTag('Y')(x) && hasTag('X')(y)
|
||||
? right(cursorPos(y.value)(x.value))
|
||||
: left(
|
||||
new PaintCursorError(
|
||||
`Both arguments provided were of the same axis, or otherwise invalid.`,
|
||||
{x, y},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
export interface CanvasOrigin extends Tag<'Origin'> {
|
||||
readonly value: CursorPos
|
||||
}
|
||||
|
||||
export interface CanvasEnd extends Tag<'End'> {
|
||||
readonly value: CursorPos
|
||||
}
|
||||
|
||||
export const origin = (pos: CursorPos): CanvasOrigin => ({
|
||||
_tag: 'Origin',
|
||||
value: pos,
|
||||
})
|
||||
|
||||
export const end = (pos: CursorPos): CanvasEnd => ({
|
||||
_tag: 'End',
|
||||
value: pos,
|
||||
})
|
||||
|
||||
export interface Canvas {
|
||||
origin: CanvasOrigin
|
||||
end: CanvasEnd
|
||||
}
|
||||
|
||||
export const canvas = (origin: CanvasOrigin) => (end: CanvasEnd): Canvas => ({
|
||||
origin,
|
||||
end,
|
||||
})
|
||||
12
src/lib/painter/errors.ts
Normal file
12
src/lib/painter/errors.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export class PaintCursorError extends Error {
|
||||
cursorX
|
||||
cursorY
|
||||
|
||||
constructor(message: string, {x, y}: {x: unknown, y: unknown}) {
|
||||
super(message)
|
||||
|
||||
this.name = this.constructor.name
|
||||
this.cursorX = !x ? 'No data' : x
|
||||
this.cursorY = !y ? 'No data' : y
|
||||
}
|
||||
}
|
||||
57
src/lib/painter/lifecycle.ts
Normal file
57
src/lib/painter/lifecycle.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { type CursorPos } from 'node:readline'
|
||||
import type { Either } from '../util/basic/either'
|
||||
import { none, type Option, some } from '../util/basic/option'
|
||||
import { type Tag } from '../util/basic/utility'
|
||||
import { type CanvasEnd, type CanvasOrigin, end, origin } from './canvas'
|
||||
|
||||
interface PainterExit<E> extends Tag<'PainterExit'> {
|
||||
value: Either<E, Error>
|
||||
}
|
||||
|
||||
interface PainterTask<E, T extends object = {}> extends Tag<'PainterTask'> {
|
||||
task: {
|
||||
origin: CanvasOrigin,
|
||||
end: CanvasEnd,
|
||||
effect: PainterEffect<E, T>,
|
||||
state: Option<T & Tag<'PainterState'>>,
|
||||
}
|
||||
}
|
||||
|
||||
type TaskConstructor = (
|
||||
x: CursorPos,
|
||||
y: CursorPos,
|
||||
) => <E = number, T extends object = {}>(
|
||||
f: PainterEffect<E, T>,
|
||||
state?: T & Tag<'PainterState'>,
|
||||
) => PainterTask<E, T>
|
||||
const task: TaskConstructor = (x, y) => (f, state?) => {
|
||||
return {
|
||||
_tag: 'PainterTask',
|
||||
task: {
|
||||
origin: origin(x),
|
||||
end: end(y),
|
||||
effect: f,
|
||||
state: !state ? none : some(state),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type ExitConstructor = <T>(x: Either<T, Error>) => PainterExit<T>
|
||||
const exit: ExitConstructor = x => ({_tag: 'PainterExit', value: x})
|
||||
|
||||
type PainterEffect<
|
||||
E,
|
||||
T extends object = {},
|
||||
> = (task: PainterTask<T>) => Promise<Either<PainterTask<E, T>, PainterExit<E>>>
|
||||
|
||||
type EffectState = <T extends object = {}>(x: T) => T & Tag<'PainterState'>
|
||||
const effectState: EffectState = x => ({_tag: 'PainterState', ...x})
|
||||
|
||||
export {
|
||||
type EffectState as PainterState,
|
||||
effectState as tagState,
|
||||
exit,
|
||||
type PainterEffect,
|
||||
type PainterTask,
|
||||
task,
|
||||
}
|
||||
46
src/lib/promptsmith/README.md
Normal file
46
src/lib/promptsmith/README.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Features to Impliment
|
||||
|
||||
## Basic Inputs
|
||||
|
||||
- [ ] **Text Input** – User types a free-form response.
|
||||
- [ ] **Password Input** – Masked input for sensitive information.
|
||||
- [ ] **Multi-line Input** – Allows extended text responses.
|
||||
- [ ] **Autocomplete** – Suggests completions based on partial input.
|
||||
|
||||
## Selection & Choice
|
||||
|
||||
- [ ] **Single Choice (Radio List)** – User selects one option from a list.
|
||||
- [ ] **Multiple Choice (Checkbox List)** – User selects multiple options.
|
||||
- [ ] **Hierarchical Menus** – Options lead to submenus for deeper selection.
|
||||
- [ ] **Searchable Select** – Allows filtering of choices dynamically.
|
||||
|
||||
## Confirmation & Boolean Responses
|
||||
|
||||
- [ ] **Yes/No Confirmation** – Quick validation prompt.
|
||||
- [ ] **Custom Boolean Toggle** – Enables/disables a setting interactively.
|
||||
|
||||
## Numeric & Range Inputs
|
||||
|
||||
- [ ] **Direct Number Input** – Accepts numeric values.
|
||||
- [ ] **Slider/Range Selector** – Interactive range selection (e.g., choose a value between 1–10).
|
||||
- [ ] **Incremental Stepper** – Adjusts numeric values up/down.
|
||||
|
||||
## Progress & Status Indicators
|
||||
|
||||
- [ ] **Loading Indicators** – Displays progress for background tasks.
|
||||
- [ ] **Spinners & Animations** – Adds visual feedback while waiting.
|
||||
- [ ] **Progress Bars** – Tracks completion percentage.
|
||||
|
||||
## Navigation & Actions
|
||||
|
||||
- [ ] **Interactive Tables** – Allows selection from tabular data.
|
||||
- [ ] **File Picker** – User selects files interactively.
|
||||
- [ ] **Date/Time Picker** – User selects a timestamp interactively.
|
||||
- [ ] **Shortcut Actions** – Fast execution based on predefined keys.
|
||||
- [ ] **Undo/Redo Prompts** – Allows correction of previous inputs.
|
||||
|
||||
## Advanced & Custom Interactions
|
||||
- [ ] **Drag-and-Drop Items (in CLI format)** – Reorder items interactively.
|
||||
- [ ] **Text-Based Dialogs** – Simulates step-by-step conversational input.
|
||||
- [ ] **Inline Editing** – Lets users modify text inline.
|
||||
- [ ] **Command Suggestions** – Auto-suggests commands based on context.
|
||||
169
src/lib/promptsmith/select-options.old.ts
Normal file
169
src/lib/promptsmith/select-options.old.ts
Normal file
@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env bun run
|
||||
import * as readline from 'node:readline';
|
||||
import { TerminalUtils, type CursorPosition } from '../util/terminal';
|
||||
import { assertArrayOfStrings } from '../util/typeCheck';
|
||||
|
||||
export type PromptsmithConfig = {
|
||||
format?: {
|
||||
selected?: string;
|
||||
selectedText?: string;
|
||||
defualt?: string;
|
||||
defualtText?: string;
|
||||
};
|
||||
selector?: {
|
||||
selected?: string;
|
||||
default?: string;
|
||||
};
|
||||
};
|
||||
|
||||
// A pure function to render options without side effects
|
||||
const formatText = (text: string, format: string): string =>
|
||||
`${format}${text}\x1B[0m`;
|
||||
|
||||
const renderOptions = (
|
||||
options: string[],
|
||||
selectedIndex: number,
|
||||
pos: CursorPosition,
|
||||
config: PromptsmithConfig
|
||||
): string[] => {
|
||||
TerminalUtils.cursor.restorePosition(pos);
|
||||
TerminalUtils.clear.below();
|
||||
|
||||
return [
|
||||
formatText(
|
||||
'Use the ↑ and ↓ keys to select an option. Press ENTER to confirm.\n',
|
||||
'\x1B[1m'
|
||||
), // Bold
|
||||
...options.map((option, index) => {
|
||||
const isSelected = index === selectedIndex;
|
||||
const selector = formatText(
|
||||
isSelected
|
||||
? (config.selector.selected as string)
|
||||
: (config.selector.default as string),
|
||||
isSelected
|
||||
? (config.format.selected as string)
|
||||
: (config.format.defualt as string)
|
||||
);
|
||||
const optionText = formatText(
|
||||
option,
|
||||
isSelected
|
||||
? (config.format.selectedText as string)
|
||||
: (config.format.defualtText as string)
|
||||
);
|
||||
return selector + optionText;
|
||||
}),
|
||||
];
|
||||
};
|
||||
|
||||
// A pure function to update selected index based on input key
|
||||
const updateSelection = (
|
||||
selectedIndex: number,
|
||||
keyName: string,
|
||||
options: string[]
|
||||
): number =>
|
||||
keyName === 'up'
|
||||
? Math.max(selectedIndex - 1, 0)
|
||||
: keyName === 'down'
|
||||
? Math.min(selectedIndex + 1, options.length - 1)
|
||||
: selectedIndex;
|
||||
|
||||
// Handles keypress events as pure transformations
|
||||
const handleKeyPress = async (
|
||||
options: string[],
|
||||
selectedIndex: number,
|
||||
pos: CursorPosition,
|
||||
config: PromptsmithConfig
|
||||
): Promise<string> => {
|
||||
return new Promise((resolve) => {
|
||||
if (!assertArrayOfStrings(options))
|
||||
throw new TypeError("'options' should be Array<string>.");
|
||||
|
||||
process.stdin.resume();
|
||||
readline.emitKeypressEvents(process.stdin);
|
||||
|
||||
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
||||
|
||||
const onKeyPress = async (_: string, key: readline.Key) => {
|
||||
const newIndex = updateSelection(
|
||||
selectedIndex,
|
||||
key.name as string,
|
||||
options
|
||||
);
|
||||
|
||||
if (key.name === 'return') {
|
||||
cleanup();
|
||||
resolve(options[newIndex] as string);
|
||||
return;
|
||||
} else if (key.name === 'c' && key.ctrl) {
|
||||
cleanup();
|
||||
process.exit();
|
||||
}
|
||||
|
||||
renderOptions(options, newIndex, pos, config).forEach((line) =>
|
||||
console.log(line)
|
||||
);
|
||||
selectedIndex = newIndex;
|
||||
};
|
||||
|
||||
process.stdin.on('keypress', onKeyPress);
|
||||
|
||||
const cleanup = () => {
|
||||
process.stdin.removeListener('keypress', onKeyPress);
|
||||
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
||||
TerminalUtils.cursor.restorePosition(pos);
|
||||
TerminalUtils.clear.below();
|
||||
process.stdin.pause();
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const setConfig = (config: PromptsmithConfig) => {
|
||||
if (!config.format) config.format = {};
|
||||
|
||||
if (!config.format.selected) config.format.selected = '\x1B[32m\x1B[1m';
|
||||
if (!config.format.selectedText)
|
||||
config.format.selectedText = config.format.selected;
|
||||
if (!config.format.defualt) config.format.defualt = '';
|
||||
if (!config.format.defualtText)
|
||||
config.format.defualtText = config.format.defualt;
|
||||
|
||||
if (!config.selector) config.selector = {};
|
||||
if (!config.selector.default) config.selector.default = ' ';
|
||||
if (!config.selector.selected) config.selector.selected = '> ';
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
// Main function using pure composition
|
||||
const defaultConfig: PromptsmithConfig = {
|
||||
format: {
|
||||
selected: '\x1B[32m\x1B[1m',
|
||||
},
|
||||
selector: {
|
||||
selected: '> ',
|
||||
default: ' ',
|
||||
},
|
||||
};
|
||||
const interactiveSelect = async (
|
||||
options: string[],
|
||||
config: PromptsmithConfig = {}
|
||||
): Promise<string> => {
|
||||
const newConfig = setConfig(config);
|
||||
|
||||
const pos = await TerminalUtils.cursor.getCursorPosition();
|
||||
renderOptions(options, 0, pos, newConfig).forEach((line) =>
|
||||
console.log(line)
|
||||
);
|
||||
return handleKeyPress(options, 0, pos, newConfig);
|
||||
};
|
||||
|
||||
// Execution
|
||||
(async () => {
|
||||
const options = ['Option A', 'Option B', 'Option C'];
|
||||
const selectedOption = await interactiveSelect(options, {
|
||||
selector: { selected: '* ' },
|
||||
});
|
||||
console.log(`You selected: ${selectedOption}`);
|
||||
|
||||
process.stdin.pause(); // Ensures stdin closes AFTER selection
|
||||
})();
|
||||
180
src/lib/promptsmith/select-options.ts
Normal file
180
src/lib/promptsmith/select-options.ts
Normal file
@ -0,0 +1,180 @@
|
||||
import * as readline from 'node:readline'
|
||||
import { emitKeypressEvents, type Key } from 'node:readline'
|
||||
import {
|
||||
exit as EXIT,
|
||||
type PainterEffect,
|
||||
type PainterState,
|
||||
type PainterTask,
|
||||
tagState,
|
||||
task as TASK,
|
||||
} from '../painter/lifecycle'
|
||||
import {
|
||||
type AnsiBufferKey,
|
||||
ansiText,
|
||||
emptyBytes,
|
||||
writeAnsiU,
|
||||
} from '../util/ansi'
|
||||
import { DoEither, type Either, left, right } from '../util/basic/either'
|
||||
import { DoOption, getOrElse, map, none, type Option, some } from '../util/basic/option'
|
||||
import { hasTag, type Tag } from '../util/basic/utility'
|
||||
import type { RecursiveRequired } from '../util/types'
|
||||
|
||||
interface SelectOptions {
|
||||
keyup: Array<Key>
|
||||
keydown: Array<Key>
|
||||
exit: Array<Key>
|
||||
accept: Array<Key>
|
||||
selector: {selected: string, default: string}
|
||||
selected: number
|
||||
options: Array<string>
|
||||
header?: {text: string, format: Array<AnsiBufferKey | Uint8Array>}
|
||||
format: {
|
||||
text: {
|
||||
default: Array<AnsiBufferKey | Uint8Array>,
|
||||
defaultSelected: Array<AnsiBufferKey | Uint8Array>,
|
||||
},
|
||||
selector: {
|
||||
default: Array<AnsiBufferKey | Uint8Array>,
|
||||
defaultSelected: Array<AnsiBufferKey | Uint8Array>,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type PartialSelectOptions =
|
||||
& Partial<Omit<SelectOptions, 'options' | 'header'>>
|
||||
& {
|
||||
options: SelectOptions['options'],
|
||||
header?: {
|
||||
text: NonNullable<SelectOptions['header']>['text'],
|
||||
format?: NonNullable<SelectOptions['header']>['format'],
|
||||
},
|
||||
}
|
||||
|
||||
type SelectOptionsConfig = (x: PartialSelectOptions) => SelectOptions
|
||||
const selectOptionsConfig: SelectOptionsConfig = x => ({
|
||||
keyup: x.keyup ?? [{name: 'up'}, {name: 'k'}],
|
||||
keydown: x.keydown ?? [{name: 'down'}, {name: 'j'}],
|
||||
exit: x.exit ?? [{name: 'esc'}],
|
||||
accept: x.accept ?? [{name: 'return'}],
|
||||
selector: {
|
||||
selected: x.selector?.selected ?? '> ',
|
||||
default: x.selector?.default ?? ' ',
|
||||
},
|
||||
selected: x.selected ?? 0,
|
||||
options: x.options,
|
||||
header: !x.header ? undefined
|
||||
: {text: x.header.text, format: x.header.format ?? ['WHITE']},
|
||||
format: {
|
||||
text: {
|
||||
default: x.format?.text?.default ?? ['WHITE'],
|
||||
defaultSelected: x.format?.text?.defaultSelected ?? ['WHITE', 'BOLD'],
|
||||
},
|
||||
selector: {
|
||||
default: x.format?.selector?.default ?? x.format?.text?.default
|
||||
?? ['WHITE'],
|
||||
defaultSelected: x.format?.selector?.defaultSelected ?? ['BLUE', 'BOLD'],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
interface Event extends Tag<'KEYUP' | 'KEYDOWN' | 'EXIT' | 'ACCEPT'> {}
|
||||
const EVENT: Record<string, Event> = {
|
||||
keyup: {_tag: 'KEYUP'},
|
||||
keydown: {_tag: 'KEYDOWN'},
|
||||
exit: {_tag: 'EXIT'},
|
||||
accept: {_tag: 'ACCEPT'},
|
||||
}
|
||||
|
||||
type KeyCheck = (x: Key) => (y: Key) => boolean
|
||||
const keyCheck: KeyCheck = x => y =>
|
||||
x.name !== y.name ? false
|
||||
: x.ctrl !== y.ctrl ? false
|
||||
: x.shift !== y.shift ? false
|
||||
: x.meta !== y.meta ? false
|
||||
: true
|
||||
|
||||
type OnKeypress =
|
||||
(x: Pick<SelectOptions, 'keyup' | 'keydown' | 'exit' | 'accept'>) =>
|
||||
(y: Key) => Option<Event>
|
||||
const onKeypress: OnKeypress = x => y =>
|
||||
x.keyup.some(a => keyCheck(a)(y)) ? some(EVENT.keyup) as Option<Event>
|
||||
: x.keydown.some(a => keyCheck(a)(y)) ? some(EVENT.keydown) as Option<Event>
|
||||
: x.exit.some(a => keyCheck(a)(y)) ? some(EVENT.exit) as Option<Event>
|
||||
: x.accept.some(a => keyCheck(a)(y)) ? some(EVENT.accept) as Option<Event>
|
||||
: none
|
||||
|
||||
type KeyListener =
|
||||
(x: Pick<SelectOptions, 'keyup' | 'keydown' | 'exit' | 'accept'>) => Promise<
|
||||
Event
|
||||
>
|
||||
const keyListener: KeyListener = x => {
|
||||
readline.emitKeypressEvents(process.stdin)
|
||||
if (process.stdin.isTTY) process.stdin.setRawMode(true)
|
||||
return new Promise((resolve) => {
|
||||
process.stdin.on(
|
||||
'keypress',
|
||||
a => onKeypress(x)(a) ? resolve(onKeypress(x)(a)) : none,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
type RenderText = (x: SelectOptions & Tag<'PainterState'>) => Promise<
|
||||
Either<number, Error>
|
||||
>
|
||||
const renderText: RenderText = async (x) => {
|
||||
try {
|
||||
return right(
|
||||
await writeAnsiU([
|
||||
...(!x.header ? [emptyBytes]
|
||||
: [...x.header.format, ansiText(x.header.text)]),
|
||||
...x.options.flatMap(i =>
|
||||
(x.options[x.selected] === i)
|
||||
? [
|
||||
...x.format.selector.defaultSelected,
|
||||
ansiText(x.selector.selected),
|
||||
'RESET' as AnsiBufferKey,
|
||||
...x.format.text.defaultSelected,
|
||||
ansiText(i + '\n'),
|
||||
'RESET' as AnsiBufferKey,
|
||||
] : [
|
||||
...x.format.selector.default,
|
||||
ansiText(x.selector.default),
|
||||
'RESET' as AnsiBufferKey,
|
||||
...x.format.text.default,
|
||||
ansiText(i + '\n'),
|
||||
'RESET' as AnsiBufferKey,
|
||||
]
|
||||
),
|
||||
]),
|
||||
)
|
||||
} catch (e) {
|
||||
return left(e as Error)
|
||||
}
|
||||
}
|
||||
|
||||
const selectOptions: PainterEffect<SelectOptions> = async (x) => {
|
||||
const render = renderText(x)
|
||||
const event = DoOption.start()
|
||||
.bind('state', x.task.state)
|
||||
.
|
||||
}
|
||||
|
||||
renderText(
|
||||
tagState(
|
||||
selectOptionsConfig({
|
||||
options: ['Option one', 'Option two', 'Option three'],
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
const test = selectOptionsConfig({
|
||||
options: ['on', 'two', 'three'],
|
||||
})
|
||||
console.log(
|
||||
onKeypress({
|
||||
keyup: test.keyup,
|
||||
keydown: test.keydown,
|
||||
exit: test.exit,
|
||||
accept: test.accept,
|
||||
})({name: 'return'}),
|
||||
)
|
||||
147
src/lib/util/ansi.ts
Normal file
147
src/lib/util/ansi.ts
Normal file
@ -0,0 +1,147 @@
|
||||
const encoder = new TextEncoder()
|
||||
const emptyBytes = encoder.encode('')
|
||||
type Writer = (x: Uint8Array | Array<Uint8Array>) => Promise<number>
|
||||
const writer: Writer = x => Bun.write(Bun.stdout, x)
|
||||
|
||||
const ANSI = {
|
||||
// Text Styles
|
||||
RESET: '\x1B[0m',
|
||||
BOLD: '\x1B[1m',
|
||||
DIM: '\x1B[2m',
|
||||
ITALIC: '\x1B[3m',
|
||||
UNDERLINE: '\x1B[4m',
|
||||
BLINK: '\x1B[5m',
|
||||
REVERSE: '\x1B[7m',
|
||||
HIDDEN: '\x1B[8m',
|
||||
|
||||
// Foreground Colors
|
||||
BLACK: '\x1B[30m',
|
||||
RED: '\x1B[31m',
|
||||
GREEN: '\x1B[32m',
|
||||
YELLOW: '\x1B[33m',
|
||||
BLUE: '\x1B[34m',
|
||||
MAGENTA: '\x1B[35m',
|
||||
CYAN: '\x1B[36m',
|
||||
WHITE: '\x1B[37m',
|
||||
|
||||
// Background Colors
|
||||
BG_BLACK: '\x1B[40m',
|
||||
BG_RED: '\x1B[41m',
|
||||
BG_GREEN: '\x1B[42m',
|
||||
BG_YELLOW: '\x1B[43m',
|
||||
BG_BLUE: '\x1B[44m',
|
||||
BG_MAGENTA: '\x1B[45m',
|
||||
BG_CYAN: '\x1B[46m',
|
||||
BG_WHITE: '\x1B[47m',
|
||||
|
||||
// Cursor Control
|
||||
GET_CURSOR_POSITION: '\x1B[6n',
|
||||
SAVE_CURSOR: '\x1B[s',
|
||||
RESTORE_CURSOR: '\x1B[u',
|
||||
CURSOR_TO: (x: number, y: number) => `\x1B[${y};${x}H`,
|
||||
CURSOR_UP: (n = 1) => `\x1B[${n}A`,
|
||||
CURSOR_DOWN: (n = 1) => `\x1B[${n}B`,
|
||||
CURSOR_FORWARD: (n = 1) => `\x1B[${n}C`,
|
||||
CURSOR_BACK: (n = 1) => `\x1B[${n}D`,
|
||||
|
||||
// Screen Control
|
||||
CLEAR_SCREEN: '\x1B[2J',
|
||||
CLEAR_LINE: '\x1B[2K',
|
||||
CLEAR_END_LINE: '\x1B[0K',
|
||||
CLEAR_START_LINE: '\x1B[1K',
|
||||
CLEAR_TO_END: '\x1B[0J', // From cursor to end of screen
|
||||
CLEAR_TO_START: '\x1B[1J', // From cursor to beginning of screen
|
||||
CLEAR_BELOW: '\x1B[J',
|
||||
}
|
||||
|
||||
const ANSI_BUFFERS = {
|
||||
// Static code buffers
|
||||
RESET: encoder.encode(ANSI.RESET),
|
||||
BOLD: encoder.encode(ANSI.BOLD),
|
||||
DIM: encoder.encode(ANSI.DIM),
|
||||
ITALIC: encoder.encode(ANSI.ITALIC),
|
||||
UNDERLINE: encoder.encode(ANSI.UNDERLINE),
|
||||
BLINK: encoder.encode(ANSI.BLINK),
|
||||
REVERSE: encoder.encode(ANSI.REVERSE),
|
||||
HIDDEN: encoder.encode(ANSI.HIDDEN),
|
||||
|
||||
// Colors
|
||||
BLACK: encoder.encode(ANSI.BLACK),
|
||||
RED: encoder.encode(ANSI.RED),
|
||||
GREEN: encoder.encode(ANSI.GREEN),
|
||||
YELLOW: encoder.encode(ANSI.YELLOW),
|
||||
BLUE: encoder.encode(ANSI.BLUE),
|
||||
MAGENTA: encoder.encode(ANSI.MAGENTA),
|
||||
CYAN: encoder.encode(ANSI.CYAN),
|
||||
WHITE: encoder.encode(ANSI.WHITE),
|
||||
|
||||
// Backgrounds
|
||||
BG_BLACK: encoder.encode(ANSI.BG_BLACK),
|
||||
BG_RED: encoder.encode(ANSI.BG_RED),
|
||||
BG_GREEN: encoder.encode(ANSI.BG_GREEN),
|
||||
BG_YELLOW: encoder.encode(ANSI.BG_YELLOW),
|
||||
BG_BLUE: encoder.encode(ANSI.BG_BLUE),
|
||||
BG_MAGENTA: encoder.encode(ANSI.BG_MAGENTA),
|
||||
BG_CYAN: encoder.encode(ANSI.BG_CYAN),
|
||||
BG_WHITE: encoder.encode(ANSI.BG_WHITE),
|
||||
|
||||
// Cursor Control
|
||||
GET_CURSOR_POSITION: encoder.encode(ANSI.GET_CURSOR_POSITION),
|
||||
SAVE_CURSOR: encoder.encode(ANSI.SAVE_CURSOR),
|
||||
RESTORE_CURSOR: encoder.encode(ANSI.RESTORE_CURSOR),
|
||||
|
||||
// Screen Control
|
||||
CLEAR_SCREEN: encoder.encode(ANSI.CLEAR_SCREEN),
|
||||
CLEAR_LINE: encoder.encode(ANSI.CLEAR_LINE),
|
||||
CLEAR_TO_END: encoder.encode(ANSI.CLEAR_TO_END),
|
||||
CLEAR_TO_START: encoder.encode(ANSI.CLEAR_TO_START),
|
||||
CLEAR_BELOW: encoder.encode(ANSI.CLEAR_BELOW),
|
||||
}
|
||||
|
||||
const ANSI_DYNAMIC = {
|
||||
// Cursor Control
|
||||
CURSOR_TO: (x: number, y: number) => encoder.encode(ANSI.CURSOR_TO(x, y)),
|
||||
CURSOR_UP: (x = 1) => encoder.encode(ANSI.CURSOR_UP(x)),
|
||||
CURSOR_DOWN: (x = 1) => encoder.encode(ANSI.CURSOR_DOWN(x)),
|
||||
CURSOR_FORWARD: (x = 1) => encoder.encode(ANSI.CURSOR_FORWARD(x)),
|
||||
CURSOR_BACK: (x = 1) => encoder.encode(ANSI.CURSOR_BACK(x)),
|
||||
}
|
||||
|
||||
type AnsiBufferKey = keyof typeof ANSI_BUFFERS
|
||||
|
||||
type WriteAnsi = (
|
||||
c: Array<(AnsiBufferKey | Uint8Array)>,
|
||||
t?: string,
|
||||
a?: boolean,
|
||||
) => Promise<number>
|
||||
const writeAnsi: WriteAnsi = (codes, text?, autoReset = true) => {
|
||||
const arr = [
|
||||
...codes,
|
||||
!text ? emptyBytes : encoder.encode(text),
|
||||
autoReset ? ANSI_BUFFERS['RESET'] : emptyBytes,
|
||||
].map(x => x instanceof Uint8Array ? x : ANSI_BUFFERS[x])
|
||||
|
||||
return writer(Buffer.concat(arr))
|
||||
}
|
||||
|
||||
type AnsiText = (x: string) => Uint8Array
|
||||
const ansiText: AnsiText = x => encoder.encode(x)
|
||||
|
||||
type WriteAnsiU = (x: Array<AnsiBufferKey | Uint8Array>) => Promise<number>
|
||||
const writeAnsiU: WriteAnsiU = x =>
|
||||
writer(Buffer.concat(x.map(
|
||||
x => x instanceof Uint8Array ? x : ANSI_BUFFERS[x],
|
||||
)))
|
||||
|
||||
// For CommonJS compatibility
|
||||
export {
|
||||
ANSI,
|
||||
ANSI_BUFFERS,
|
||||
ANSI_DYNAMIC,
|
||||
type AnsiBufferKey,
|
||||
ansiText,
|
||||
emptyBytes,
|
||||
writeAnsi,
|
||||
writeAnsiU,
|
||||
writer,
|
||||
}
|
||||
68
src/lib/util/basic/either.ts
Normal file
68
src/lib/util/basic/either.ts
Normal file
@ -0,0 +1,68 @@
|
||||
export type Left<E> = {readonly _tag: 'Left', readonly left: E}
|
||||
export type Right<A> = {readonly _tag: 'Right', readonly right: A}
|
||||
export type Either<A, E> = Right<A> | Left<E>
|
||||
|
||||
// Constructors
|
||||
export const left = <E>(e: E): Either<never, E> => ({
|
||||
_tag: 'Left',
|
||||
left: e,
|
||||
})
|
||||
export const right = <A>(a: A): Either<A, never> => ({
|
||||
_tag: 'Right',
|
||||
right: a,
|
||||
})
|
||||
|
||||
// Operations
|
||||
export const eitherMap =
|
||||
<A, B, E>(f: (a: A) => B) => (fa: Either<A, E>): Either<B, E> =>
|
||||
fa._tag === 'Right' ? right(f(fa.right)) : fa
|
||||
|
||||
export const eitherChain =
|
||||
<A, B, E>(f: (a: A) => Either<B, E>) => (fa: Either<A, E>): Either<B, E> =>
|
||||
fa._tag === 'Right' ? f(fa.right) : fa
|
||||
|
||||
export const fold =
|
||||
<A, E, B>(onLeft: (e: E) => B, onRight: (a: A) => B) =>
|
||||
(fa: Either<A, E>): B =>
|
||||
fa._tag === 'Left' ? onLeft(fa.left) : onRight(fa.right)
|
||||
|
||||
// Do notation
|
||||
export class DoEither<T extends Record<string, unknown>, E> {
|
||||
constructor(private readonly either: Either<T, E>) {}
|
||||
|
||||
bind<K extends string, A>(
|
||||
key: K,
|
||||
fa: Either<A, E>,
|
||||
): DoEither<T & Record<K, A>, E> {
|
||||
const newEither = eitherChain<T, T & Record<K, A>, E>((scope: T) =>
|
||||
eitherMap<A, T & Record<K, A>, E>((a: A) => ({...scope, [key]: a}))(fa)
|
||||
)(this.either)
|
||||
return new DoEither(newEither)
|
||||
}
|
||||
|
||||
return<B>(f: (scope: T) => B): Either<B, E> {
|
||||
return eitherMap<T, B, E>(f)(this.either)
|
||||
}
|
||||
|
||||
done(): Either<T, E> {
|
||||
return this.either
|
||||
}
|
||||
|
||||
do(fa: Either<unknown, E>): DoEither<T, E> {
|
||||
const newEither = eitherChain<T, T, E>((scope: T) =>
|
||||
eitherMap<unknown, T, E>(() => scope)(fa)
|
||||
)(this.either)
|
||||
return new DoEither(newEither)
|
||||
}
|
||||
|
||||
doL(f: (scope: T) => Either<unknown, E>): DoEither<T, E> {
|
||||
const newEither = eitherChain<T, T, E>((scope: T) =>
|
||||
eitherMap<unknown, T, E>(() => scope)(f(scope))
|
||||
)(this.either)
|
||||
return new DoEither(newEither)
|
||||
}
|
||||
|
||||
static start<E>(): DoEither<{}, E> {
|
||||
return new DoEither(right({}))
|
||||
}
|
||||
}
|
||||
43
src/lib/util/basic/list.ts
Normal file
43
src/lib/util/basic/list.ts
Normal file
@ -0,0 +1,43 @@
|
||||
export type Nil = {readonly _tag: 'Nil'}
|
||||
export type Cons<A> = {
|
||||
readonly _tag: 'Cons',
|
||||
readonly head: A,
|
||||
readonly tail: List<A>,
|
||||
}
|
||||
export type List<A> = Nil | Cons<A>
|
||||
|
||||
// Constructors
|
||||
export const nil: Nil = {_tag: 'Nil'}
|
||||
export const cons = <A>(head: A, tail: List<A>): List<A> => ({
|
||||
_tag: 'Cons',
|
||||
head,
|
||||
tail,
|
||||
})
|
||||
|
||||
// Operations
|
||||
type ListMap = <A, B>(f: (a: A) => B) => (fa: List<A>) => List<B>
|
||||
export const listMap: ListMap = f => fa =>
|
||||
fa._tag === 'Cons' ? cons(f(fa.head), listMap(f)(fa.tail)) : nil
|
||||
|
||||
type ListReduce = <A, B>(
|
||||
f: (b: B, a: A) => B,
|
||||
initial: B,
|
||||
) => (fa: List<A>) => B
|
||||
export const listReduce: ListReduce = (f, initial) => fa =>
|
||||
fa._tag === 'Cons' ? listReduce(f, f(initial, fa.head))(fa.tail)
|
||||
: initial
|
||||
|
||||
// Helpers
|
||||
type FromArray = <A>(arr: Array<A>) => List<A>
|
||||
export const fromArray: FromArray = <A>(arr: Array<A>) =>
|
||||
arr.reduceRight(
|
||||
(acc: List<A>, val: A) => cons(val, acc),
|
||||
nil as List<A>,
|
||||
)
|
||||
|
||||
type ToArray = <A>(fa: List<A>) => Array<A>
|
||||
export const toArray: ToArray = <A>(fa: List<A>) =>
|
||||
listReduce<A, Array<A>>(
|
||||
(acc, val) => [...acc, val],
|
||||
[] as Array<A>,
|
||||
)(fa)
|
||||
55
src/lib/util/basic/option.ts
Normal file
55
src/lib/util/basic/option.ts
Normal file
@ -0,0 +1,55 @@
|
||||
export type None = {readonly _tag: 'None'}
|
||||
export type Some<A> = {readonly _tag: 'Some', readonly value: A}
|
||||
export type Option<A> = Some<A> | None
|
||||
|
||||
// Constructors
|
||||
export const none: Option<never> = {_tag: 'None'}
|
||||
export const some = <A>(value: A): Option<A> => ({
|
||||
_tag: 'Some',
|
||||
value,
|
||||
})
|
||||
|
||||
// Operations
|
||||
export const map = <A, B>(f: (a: A) => B) => (fa: Option<A>): Option<B> =>
|
||||
fa._tag === 'Some' ? some(f(fa.value)) : none
|
||||
|
||||
export const chain =
|
||||
<A, B>(f: (a: A) => Option<B>) => (fa: Option<A>): Option<B> =>
|
||||
fa._tag === 'Some' ? f(fa.value) : none
|
||||
|
||||
export const getOrElse = <A>(defaultValue: A) => (fa: Option<A>): A =>
|
||||
fa._tag === 'Some' ? fa.value : defaultValue
|
||||
|
||||
export class DoOption<T extends Record<string, unknown>> {
|
||||
constructor(private readonly option: Option<T>) {}
|
||||
|
||||
bind<K extends string, A>(key: K, fa: Option<A>): DoOption<T & Record<K, A>> {
|
||||
const newOption = chain((scope: T) =>
|
||||
map((a: A) => ({...scope, [key]: a}))(fa)
|
||||
)(this.option)
|
||||
return new DoOption(newOption as Option<T & Record<K, A>>)
|
||||
}
|
||||
|
||||
return<B>(f: (scope: T) => B): Option<B> {
|
||||
return map(f)(this.option)
|
||||
}
|
||||
|
||||
done(): Option<T> {
|
||||
return this.option
|
||||
}
|
||||
do(fa: Option<unknown>): DoOption<T> {
|
||||
const newOption = chain((scope: T) => map(() => scope)(fa))(this.option)
|
||||
return new DoOption(newOption)
|
||||
}
|
||||
|
||||
doL(f: (scope: T) => Option<unknown>): DoOption<T> {
|
||||
const newOption = chain((scope: T) => map(() => scope)(f(scope)))(
|
||||
this.option,
|
||||
)
|
||||
return new DoOption(newOption)
|
||||
}
|
||||
|
||||
static start(): DoOption<{}> {
|
||||
return new DoOption(some({}))
|
||||
}
|
||||
}
|
||||
184
src/lib/util/basic/utility.ts
Normal file
184
src/lib/util/basic/utility.ts
Normal file
@ -0,0 +1,184 @@
|
||||
import { type Either, left, right } from './either'
|
||||
import { none, type Option, some } from './option'
|
||||
|
||||
interface Pipe {
|
||||
<A>(value: A): A
|
||||
<A, B>(value: A, fn1: (a: A) => B): B
|
||||
<A, B, C>(value: A, fn1: (a: A) => B, fn2: (b: B) => C): C
|
||||
<A, B, C, D>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
): D
|
||||
<A, B, C, D, E>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
fn4: (d: D) => E,
|
||||
): E
|
||||
<A, B, C, D, E, F>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
fn4: (d: D) => E,
|
||||
fn5: (e: E) => F,
|
||||
): F
|
||||
<A, B, C, D, E, F, G>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
fn4: (d: D) => E,
|
||||
fn5: (e: E) => F,
|
||||
fn6: (f: F) => G,
|
||||
): G
|
||||
<A, B, C, D, E, F, G, H>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
fn4: (d: D) => E,
|
||||
fn5: (e: E) => F,
|
||||
fn6: (f: F) => G,
|
||||
fn7: (g: G) => H,
|
||||
): H
|
||||
<A, B, C, D, E, F, G, H, I>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
fn4: (d: D) => E,
|
||||
fn5: (e: E) => F,
|
||||
fn6: (f: F) => G,
|
||||
fn7: (g: G) => H,
|
||||
fn8: (h: H) => I,
|
||||
): I
|
||||
<A, B, C, D, E, F, G, H, I, J>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
fn4: (d: D) => E,
|
||||
fn5: (e: E) => F,
|
||||
fn6: (f: F) => G,
|
||||
fn7: (g: G) => H,
|
||||
fn8: (h: H) => I,
|
||||
fn9: (i: I) => J,
|
||||
): J
|
||||
<A, B, C, D, E, F, G, H, I, J, K>(
|
||||
value: A,
|
||||
fn1: (a: A) => B,
|
||||
fn2: (b: B) => C,
|
||||
fn3: (c: C) => D,
|
||||
fn4: (d: D) => E,
|
||||
fn5: (e: E) => F,
|
||||
fn6: (f: F) => G,
|
||||
fn7: (g: G) => H,
|
||||
fn8: (h: H) => I,
|
||||
fn9: (i: I) => J,
|
||||
fn10: (j: J) => K,
|
||||
): K
|
||||
}
|
||||
|
||||
export const pipe: Pipe = (value: unknown, ...fns: Function[]): unknown => {
|
||||
return fns.reduce((acc, fn) => fn(acc), value)
|
||||
}
|
||||
|
||||
// Compose Function (reverse order)
|
||||
interface Compose {
|
||||
<A>(): (a: A) => A
|
||||
<A, B>(fn1: (a: A) => B): (a: A) => B
|
||||
<A, B, C>(fn2: (b: B) => C, fn1: (a: A) => B): (a: A) => C
|
||||
<A, B, C, D>(
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => D
|
||||
<A, B, C, D, E>(
|
||||
fn4: (d: D) => E,
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => E
|
||||
<A, B, C, D, E, F>(
|
||||
fn5: (e: E) => F,
|
||||
fn4: (d: D) => E,
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => F
|
||||
<A, B, C, D, E, F, G>(
|
||||
fn6: (f: F) => G,
|
||||
fn5: (e: E) => F,
|
||||
fn4: (d: D) => E,
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => G
|
||||
<A, B, C, D, E, F, G, H>(
|
||||
fn7: (g: G) => H,
|
||||
fn6: (f: F) => G,
|
||||
fn5: (e: E) => F,
|
||||
fn4: (d: D) => E,
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => H
|
||||
<A, B, C, D, E, F, G, H, I>(
|
||||
fn8: (h: H) => I,
|
||||
fn7: (g: G) => H,
|
||||
fn6: (f: F) => G,
|
||||
fn5: (e: E) => F,
|
||||
fn4: (d: D) => E,
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => I
|
||||
<A, B, C, D, E, F, G, H, I, J>(
|
||||
fn9: (i: I) => J,
|
||||
fn8: (h: H) => I,
|
||||
fn7: (g: G) => H,
|
||||
fn6: (f: F) => G,
|
||||
fn5: (e: E) => F,
|
||||
fn4: (d: D) => E,
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => J
|
||||
<A, B, C, D, E, F, G, H, I, J, K>(
|
||||
fn10: (j: J) => K,
|
||||
fn9: (i: I) => J,
|
||||
fn8: (h: H) => I,
|
||||
fn7: (g: G) => H,
|
||||
fn6: (f: F) => G,
|
||||
fn5: (e: E) => F,
|
||||
fn4: (d: D) => E,
|
||||
fn3: (c: C) => D,
|
||||
fn2: (b: B) => C,
|
||||
fn1: (a: A) => B,
|
||||
): (a: A) => K
|
||||
}
|
||||
|
||||
export const compose: Compose = (...fns: Function[]) => {
|
||||
return (value: unknown) => fns.reduceRight((acc, fn) => fn(acc), value)
|
||||
}
|
||||
|
||||
export const tryCatch = <A>(f: () => A): Either<A, Error> => {
|
||||
try {
|
||||
return right(f())
|
||||
} catch (e) {
|
||||
return left(e instanceof Error ? e : new Error(String(e)))
|
||||
}
|
||||
}
|
||||
|
||||
export interface Tag<T> {
|
||||
readonly _tag: T
|
||||
}
|
||||
|
||||
export const hasTag = (x: string) => (y: Tag<string>) => x === y._tag
|
||||
|
||||
export const fromNullable = <A>(a: A | null | undefined): Option<A> =>
|
||||
a == null ? none : some(a)
|
||||
55
src/lib/util/terminal.ts
Normal file
55
src/lib/util/terminal.ts
Normal file
@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
export type CursorPosition = { row: number; col: number };
|
||||
|
||||
export const TerminalUtils = {
|
||||
clear: {
|
||||
screen: () => process.stdout.write('\x1Bc'),
|
||||
content: () => process.stdout.write('\x1B[2J'),
|
||||
cursorHome: () => process.stdout.write('\x1B[H'),
|
||||
line: () => process.stdout.write('\x1B[K'),
|
||||
below: () => process.stdout.write('\x1B[J'),
|
||||
above: () => process.stdout.write('\x1B[1J'),
|
||||
clear: () => console.clear(),
|
||||
},
|
||||
|
||||
cursor: {
|
||||
/** Move cursor to a specific row & column */
|
||||
moveCursor: (row: number, col: number) =>
|
||||
process.stdout.write(`\x1B[${row};${col}H`),
|
||||
|
||||
/** Queries the terminal for the current cursor position */
|
||||
async getCursorPosition(): Promise<CursorPosition> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
||||
reject(new Error('Not running in a terminal environment!'));
|
||||
|
||||
const stdin = process.stdin;
|
||||
stdin.setRawMode(true);
|
||||
stdin.resume();
|
||||
stdin.setEncoding('utf8');
|
||||
|
||||
stdin.once('data', (data) => {
|
||||
const match = /\[(\d+);(\d+)R/.exec(data.toString());
|
||||
resolve({
|
||||
row: parseInt(match?.[1] ?? '0', 10),
|
||||
col: parseInt(match?.[2] ?? '0', 10),
|
||||
});
|
||||
stdin.setRawMode(false);
|
||||
stdin.pause();
|
||||
});
|
||||
|
||||
process.stdout.write('\x1B[6n'); // Ask terminal for cursor position
|
||||
});
|
||||
},
|
||||
|
||||
/** Stores detected cursor position in a constant */
|
||||
async storePosition(): Promise<CursorPosition> {
|
||||
return TerminalUtils.cursor.getCursorPosition();
|
||||
},
|
||||
|
||||
/** Restores the passed cursor position */
|
||||
restorePosition: (pos: CursorPosition) =>
|
||||
TerminalUtils.cursor.moveCursor(pos.row, pos.col),
|
||||
},
|
||||
};
|
||||
@ -1,6 +1,5 @@
|
||||
export function assertArrayOfStrings(arr: unknown) {
|
||||
const arrName = Object.keys({ arr })[0];
|
||||
if (!Array.isArray(arr)) return false;
|
||||
if (!arr.every((item: unknown) => typeof item === "string")) return false;
|
||||
if (!arr.every((item: unknown) => typeof item === 'string')) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
7
src/lib/util/types.ts
Normal file
7
src/lib/util/types.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export type RecursiveRequired<T> = Required<
|
||||
{
|
||||
[P in keyof T]: T[P] extends object | undefined
|
||||
? RecursiveRequired<Required<T[P]>>
|
||||
: T[P]
|
||||
}
|
||||
>
|
||||
Loading…
Reference in New Issue
Block a user