diff --git a/package-lock.json b/package-lock.json index 24eed05..3fad0e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,13 @@ "license": "MIT", "dependencies": { "@colony/core": "^2.0.1", + "@graphql-tools/executor-http": "^3.0.7", + "@graphql-tools/wrap": "^11.1.2", "cors": "^2.8.5", "express": "^4.18.2", "express-session": "^1.17.3", - "http-proxy-middleware": "^2.0.6", + "graphql-middleware": "^6.1.35", + "graphql-shield": "^7.6.5", "node-fetch": "2.6", "siwe": "^2.1.4", "ws": "^8.16.0" @@ -32,7 +35,16 @@ "ts-node": "^10.9.1", "tsc-alias": "^1.8.8", "tsconfig-paths": "^4.2.0", - "typescript": "^5.2.2" + "typescript": "^5.9.3" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, "node_modules/@colony/core": { @@ -59,6 +71,47 @@ "node": ">=12" } }, + "node_modules/@envelop/core": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@envelop/core/-/core-5.4.0.tgz", + "integrity": "sha512-/1fat63pySE8rw/dZZArEVytLD90JApY85deDJ0/34gm+yhQ3k70CloSUevxoOE4YCGveG3s9SJJfQeeB4NAtQ==", + "license": "MIT", + "dependencies": { + "@envelop/instrumentation": "^1.0.0", + "@envelop/types": "^5.2.1", + "@whatwg-node/promise-helpers": "^1.2.4", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@envelop/instrumentation": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@envelop/instrumentation/-/instrumentation-1.0.0.tgz", + "integrity": "sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw==", + "license": "MIT", + "dependencies": { + "@whatwg-node/promise-helpers": "^1.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@envelop/types": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@envelop/types/-/types-5.2.1.tgz", + "integrity": "sha512-CsFmA3u3c2QoLDTfEpGr4t25fjMU31nyvse7IzWTvb0ZycuPjMjb0fjlheh+PbhBYb9YLugnT2uY6Mwcg1o+Zg==", + "license": "MIT", + "dependencies": { + "@whatwg-node/promise-helpers": "^1.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@ethersproject/abi": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", @@ -781,6 +834,199 @@ "@ethersproject/strings": "^5.7.0" } }, + "node_modules/@fastify/busboy": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", + "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==", + "license": "MIT" + }, + "node_modules/@graphql-hive/signal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@graphql-hive/signal/-/signal-2.0.0.tgz", + "integrity": "sha512-Pz8wB3K0iU6ae9S1fWfsmJX24CcGeTo6hE7T44ucmV/ALKRj+bxClmqrYcDT7v3f0d12Rh4FAXBb6gon+WkDpQ==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@graphql-tools/batch-execute": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-10.0.4.tgz", + "integrity": "sha512-t8E0ILelbaIju0aNujMkKetUmbv3/07nxGSv0kEGLBk9GNtEmQ/Bjj8ZTo2WN35/Fy70zCHz2F/48Nx/Ec48cA==", + "license": "MIT", + "dependencies": { + "@graphql-tools/utils": "^10.10.3", + "@whatwg-node/promise-helpers": "^1.3.2", + "dataloader": "^2.2.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/delegate": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-12.0.2.tgz", + "integrity": "sha512-1X93onxNgOzRvnZ8Xulwi6gNuBeuDxvGYOjUHEZyesPCsaWsyiVj1Wk6Pw/DTPGLy70sOFUKQGcaZbWnDORM2w==", + "license": "MIT", + "dependencies": { + "@graphql-tools/batch-execute": "^10.0.4", + "@graphql-tools/executor": "^1.4.13", + "@graphql-tools/schema": "^10.0.29", + "@graphql-tools/utils": "^10.10.3", + "@repeaterjs/repeater": "^3.0.6", + "@whatwg-node/promise-helpers": "^1.3.2", + "dataloader": "^2.2.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/executor": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.5.0.tgz", + "integrity": "sha512-3HzAxfexmynEWwRB56t/BT+xYKEYLGPvJudR1jfs+XZX8bpfqujEhqVFoxmkpEE8BbFcKuBNoQyGkTi1eFJ+hA==", + "license": "MIT", + "dependencies": { + "@graphql-tools/utils": "^10.11.0", + "@graphql-typed-document-node/core": "^3.2.0", + "@repeaterjs/repeater": "^3.0.4", + "@whatwg-node/disposablestack": "^0.0.6", + "@whatwg-node/promise-helpers": "^1.0.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/executor-common": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-common/-/executor-common-1.0.5.tgz", + "integrity": "sha512-gsBRxP4ui8s7/ppKGCJUQ9xxTNoFpNYmEirgM52EHo74hL5hrpS5o4zOmBH33+9t2ZasBziIfupYtLNa0DgK0g==", + "license": "MIT", + "dependencies": { + "@envelop/core": "^5.4.0", + "@graphql-tools/utils": "^10.10.3" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/executor-http": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-3.0.7.tgz", + "integrity": "sha512-sHjtiUZmRtkjhpSzMhxT2ywAGzHjuB1rHsiaSLAq8U5BQg5WoLakKYD7BajgVHwNbfWEc+NnFiJI7ldyhiciiQ==", + "license": "MIT", + "dependencies": { + "@graphql-hive/signal": "^2.0.0", + "@graphql-tools/executor-common": "^1.0.5", + "@graphql-tools/utils": "^10.10.3", + "@repeaterjs/repeater": "^3.0.4", + "@whatwg-node/disposablestack": "^0.0.6", + "@whatwg-node/fetch": "^0.10.13", + "@whatwg-node/promise-helpers": "^1.3.2", + "meros": "^1.3.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/merge": { + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.6.tgz", + "integrity": "sha512-bTnP+4oom4nDjmkS3Ykbe+ljAp/RIiWP3R35COMmuucS24iQxGLa9Hn8VMkLIoaoPxgz6xk+dbC43jtkNsFoBw==", + "license": "MIT", + "dependencies": { + "@graphql-tools/utils": "^10.11.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/schema": { + "version": "10.0.30", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.30.tgz", + "integrity": "sha512-yPXU17uM/LR90t92yYQqn9mAJNOVZJc0nQtYeZyZeQZeQjwIGlTubvvoDL0fFVk+wZzs4YQOgds2NwSA4npodA==", + "license": "MIT", + "dependencies": { + "@graphql-tools/merge": "^9.1.6", + "@graphql-tools/utils": "^10.11.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/utils": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.11.0.tgz", + "integrity": "sha512-iBFR9GXIs0gCD+yc3hoNswViL1O5josI33dUqiNStFI/MHLCEPduasceAcazRH77YONKNiviHBV8f7OgcT4o2Q==", + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "@whatwg-node/promise-helpers": "^1.0.0", + "cross-inspect": "1.0.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/wrap": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-11.1.2.tgz", + "integrity": "sha512-TcKZzUzJNmuyMBQ1oMdnxhBUUacN/5VEJu0/1KVce2aIzCwTTaN9JTU3MgjO7l5Ixn4QLkc6XbxYNv0cHDQgtQ==", + "license": "MIT", + "dependencies": { + "@graphql-tools/delegate": "^12.0.2", + "@graphql-tools/schema": "^10.0.29", + "@graphql-tools/utils": "^10.10.3", + "@whatwg-node/promise-helpers": "^1.3.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -879,6 +1125,12 @@ "node": ">=14" } }, + "node_modules/@repeaterjs/repeater": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.6.tgz", + "integrity": "sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==", + "license": "MIT" + }, "node_modules/@spruceid/siwe-parser": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@spruceid/siwe-parser/-/siwe-parser-2.0.2.tgz", @@ -945,7 +1197,7 @@ "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "devOptional": true, + "dev": true, "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -955,7 +1207,7 @@ "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "devOptional": true, + "dev": true, "dependencies": { "@types/node": "*" } @@ -973,7 +1225,7 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "devOptional": true, + "dev": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -985,7 +1237,7 @@ "version": "4.17.41", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", - "devOptional": true, + "dev": true, "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -1016,26 +1268,25 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "devOptional": true + "dev": true }, - "node_modules/@types/http-proxy": { - "version": "1.17.14", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", - "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", - "dependencies": { - "@types/node": "*" - } + "node_modules/@types/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==", + "license": "MIT" }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "devOptional": true + "dev": true }, "node_modules/@types/node": { "version": "20.9.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "devOptional": true, "dependencies": { "undici-types": "~5.26.4" } @@ -1054,19 +1305,19 @@ "version": "6.9.10", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==", - "devOptional": true + "dev": true }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "devOptional": true + "dev": true }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "devOptional": true, + "dev": true, "dependencies": { "@types/mime": "^1", "@types/node": "*" @@ -1076,7 +1327,7 @@ "version": "1.15.5", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", - "devOptional": true, + "dev": true, "dependencies": { "@types/http-errors": "*", "@types/mime": "*", @@ -1092,6 +1343,65 @@ "@types/node": "*" } }, + "node_modules/@types/yup": { + "version": "0.29.13", + "resolved": "https://registry.npmjs.org/@types/yup/-/yup-0.29.13.tgz", + "integrity": "sha512-qRyuv+P/1t1JK1rA+elmK1MmCL1BapEzKKfbEhDBV/LMMse4lmhZ/XbgETI39JveDJRpLjmToOI6uFtMW/WR2g==", + "license": "MIT" + }, + "node_modules/@whatwg-node/disposablestack": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@whatwg-node/disposablestack/-/disposablestack-0.0.6.tgz", + "integrity": "sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw==", + "license": "MIT", + "dependencies": { + "@whatwg-node/promise-helpers": "^1.0.0", + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@whatwg-node/fetch": { + "version": "0.10.13", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.13.tgz", + "integrity": "sha512-b4PhJ+zYj4357zwk4TTuF2nEe0vVtOrwdsrNo5hL+u1ojXNhh1FgJ6pg1jzDlwlT4oBdzfSwaBwMCtFCsIWg8Q==", + "license": "MIT", + "dependencies": { + "@whatwg-node/node-fetch": "^0.8.3", + "urlpattern-polyfill": "^10.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@whatwg-node/node-fetch": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.8.4.tgz", + "integrity": "sha512-AlKLc57loGoyYlrzDbejB9EeR+pfdJdGzbYnkEuZaGekFboBwzfVYVMsy88PMriqPI1ORpiGYGgSSWpx7a2sDA==", + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^3.1.1", + "@whatwg-node/disposablestack": "^0.0.6", + "@whatwg-node/promise-helpers": "^1.3.2", + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@whatwg-node/promise-helpers": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz", + "integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1246,6 +1556,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -1402,6 +1713,18 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cross-inspect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz", + "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1416,6 +1739,12 @@ "node": ">= 8" } }, + "node_modules/dataloader": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz", + "integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==", + "license": "MIT" + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1603,11 +1932,6 @@ "@ethersproject/wordlists": "5.7.0" } }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -1741,6 +2065,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -1765,25 +2090,6 @@ "node": ">= 0.8" } }, - "node_modules/follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -1959,11 +2265,123 @@ "version": "16.8.1", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", - "dev": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, + "node_modules/graphql-middleware": { + "version": "6.1.35", + "resolved": "https://registry.npmjs.org/graphql-middleware/-/graphql-middleware-6.1.35.tgz", + "integrity": "sha512-azawK7ApUYtcuPGRGBR9vDZu795pRuaFhO5fgomdJppdfKRt7jwncuh0b7+D3i574/4B+16CNWgVpnGVlg3ZCg==", + "license": "MIT", + "dependencies": { + "@graphql-tools/delegate": "^8.8.1", + "@graphql-tools/schema": "^8.5.1" + }, + "peerDependencies": { + "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, + "node_modules/graphql-middleware/node_modules/@graphql-tools/batch-execute": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-8.5.1.tgz", + "integrity": "sha512-hRVDduX0UDEneVyEWtc2nu5H2PxpfSfM/riUlgZvo/a/nG475uyehxR5cFGvTEPEQUKY3vGIlqvtRigzqTfCew==", + "license": "MIT", + "dependencies": { + "@graphql-tools/utils": "8.9.0", + "dataloader": "2.1.0", + "tslib": "^2.4.0", + "value-or-promise": "1.0.11" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/graphql-middleware/node_modules/@graphql-tools/delegate": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-8.8.1.tgz", + "integrity": "sha512-NDcg3GEQmdEHlnF7QS8b4lM1PSF+DKeFcIlLEfZFBvVq84791UtJcDj8734sIHLukmyuAxXMfA1qLd2l4lZqzA==", + "license": "MIT", + "dependencies": { + "@graphql-tools/batch-execute": "8.5.1", + "@graphql-tools/schema": "8.5.1", + "@graphql-tools/utils": "8.9.0", + "dataloader": "2.1.0", + "tslib": "~2.4.0", + "value-or-promise": "1.0.11" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/graphql-middleware/node_modules/@graphql-tools/merge": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.1.tgz", + "integrity": "sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg==", + "license": "MIT", + "dependencies": { + "@graphql-tools/utils": "8.9.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/graphql-middleware/node_modules/@graphql-tools/schema": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-8.5.1.tgz", + "integrity": "sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg==", + "license": "MIT", + "dependencies": { + "@graphql-tools/merge": "8.3.1", + "@graphql-tools/utils": "8.9.0", + "tslib": "^2.4.0", + "value-or-promise": "1.0.11" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/graphql-middleware/node_modules/@graphql-tools/utils": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.9.0.tgz", + "integrity": "sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/graphql-middleware/node_modules/dataloader": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.1.0.tgz", + "integrity": "sha512-qTcEYLen3r7ojZNgVUaRggOI+KM7jrKxXeSHhogh/TWxYMeONEMqY+hmkobiYQozsGIyg9OYVzO4ZIfoB4I0pQ==", + "license": "MIT" + }, + "node_modules/graphql-middleware/node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "license": "0BSD" + }, + "node_modules/graphql-shield": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/graphql-shield/-/graphql-shield-7.6.5.tgz", + "integrity": "sha512-etbzf7UIhQW6vadn/UR+ds0LJOceO8ITDXwbUkQMlP2KqPgSKTZRE2zci+AUfqP+cpV9zDQdbTJfPfW5OCEamg==", + "license": "MIT", + "dependencies": { + "@types/yup": "0.29.13", + "object-hash": "^3.0.0", + "tslib": "^2.4.0", + "yup": "^0.32.0" + }, + "peerDependencies": { + "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", + "graphql-middleware": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^6.0.0" + } + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2053,42 +2471,6 @@ "node": ">= 0.8" } }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -2144,6 +2526,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -2161,6 +2544,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -2172,21 +2556,11 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "engines": { "node": ">=0.12.0" } }, - "node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2229,6 +2603,18 @@ "node": ">=6" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2269,6 +2655,23 @@ "node": ">= 8" } }, + "node_modules/meros": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/meros/-/meros-1.3.2.tgz", + "integrity": "sha512-Q3mobPbvEx7XbwhnC1J1r60+5H6EZyNccdzSz0eGexJRwouUtTZxPVRGdqKtxlpD84ScK4+tIGldkqDtCKdI0A==", + "license": "MIT", + "engines": { + "node": ">=13" + }, + "peerDependencies": { + "@types/node": ">=13" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -2281,6 +2684,7 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -2379,6 +2783,12 @@ "url": "https://github.com/sponsors/raouldeheer" } }, + "node_modules/nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==", + "license": "MIT" + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -2481,6 +2891,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -2568,6 +2987,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -2587,6 +3007,12 @@ "node": ">=12" } }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", + "license": "MIT" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -2684,11 +3110,6 @@ "node": ">=8.10.0" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -3050,6 +3471,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -3065,6 +3487,12 @@ "node": ">=0.6" } }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", + "license": "MIT" + }, "node_modules/touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", @@ -3156,6 +3584,12 @@ "node": ">=6" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -3169,10 +3603,11 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3201,7 +3636,8 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "devOptional": true }, "node_modules/unpipe": { "version": "1.0.0", @@ -3219,6 +3655,12 @@ "punycode": "^2.1.0" } }, + "node_modules/urlpattern-polyfill": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz", + "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==", + "license": "MIT" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -3238,6 +3680,15 @@ "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", "integrity": "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==" }, + "node_modules/value-or-promise": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz", + "integrity": "sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3394,9 +3845,32 @@ "engines": { "node": ">=6" } + }, + "node_modules/yup": { + "version": "0.32.11", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", + "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + }, + "engines": { + "node": ">=10" + } } }, "dependencies": { + "@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==" + }, "@colony/core": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@colony/core/-/core-2.0.1.tgz", @@ -3412,6 +3886,35 @@ "@jridgewell/trace-mapping": "0.3.9" } }, + "@envelop/core": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@envelop/core/-/core-5.4.0.tgz", + "integrity": "sha512-/1fat63pySE8rw/dZZArEVytLD90JApY85deDJ0/34gm+yhQ3k70CloSUevxoOE4YCGveG3s9SJJfQeeB4NAtQ==", + "requires": { + "@envelop/instrumentation": "^1.0.0", + "@envelop/types": "^5.2.1", + "@whatwg-node/promise-helpers": "^1.2.4", + "tslib": "^2.5.0" + } + }, + "@envelop/instrumentation": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@envelop/instrumentation/-/instrumentation-1.0.0.tgz", + "integrity": "sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw==", + "requires": { + "@whatwg-node/promise-helpers": "^1.2.1", + "tslib": "^2.5.0" + } + }, + "@envelop/types": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@envelop/types/-/types-5.2.1.tgz", + "integrity": "sha512-CsFmA3u3c2QoLDTfEpGr4t25fjMU31nyvse7IzWTvb0ZycuPjMjb0fjlheh+PbhBYb9YLugnT2uY6Mwcg1o+Zg==", + "requires": { + "@whatwg-node/promise-helpers": "^1.0.0", + "tslib": "^2.5.0" + } + }, "@ethersproject/abi": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", @@ -3822,6 +4325,128 @@ "@ethersproject/strings": "^5.7.0" } }, + "@fastify/busboy": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", + "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==" + }, + "@graphql-hive/signal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@graphql-hive/signal/-/signal-2.0.0.tgz", + "integrity": "sha512-Pz8wB3K0iU6ae9S1fWfsmJX24CcGeTo6hE7T44ucmV/ALKRj+bxClmqrYcDT7v3f0d12Rh4FAXBb6gon+WkDpQ==" + }, + "@graphql-tools/batch-execute": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-10.0.4.tgz", + "integrity": "sha512-t8E0ILelbaIju0aNujMkKetUmbv3/07nxGSv0kEGLBk9GNtEmQ/Bjj8ZTo2WN35/Fy70zCHz2F/48Nx/Ec48cA==", + "requires": { + "@graphql-tools/utils": "^10.10.3", + "@whatwg-node/promise-helpers": "^1.3.2", + "dataloader": "^2.2.3", + "tslib": "^2.8.1" + } + }, + "@graphql-tools/delegate": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-12.0.2.tgz", + "integrity": "sha512-1X93onxNgOzRvnZ8Xulwi6gNuBeuDxvGYOjUHEZyesPCsaWsyiVj1Wk6Pw/DTPGLy70sOFUKQGcaZbWnDORM2w==", + "requires": { + "@graphql-tools/batch-execute": "^10.0.4", + "@graphql-tools/executor": "^1.4.13", + "@graphql-tools/schema": "^10.0.29", + "@graphql-tools/utils": "^10.10.3", + "@repeaterjs/repeater": "^3.0.6", + "@whatwg-node/promise-helpers": "^1.3.2", + "dataloader": "^2.2.3", + "tslib": "^2.8.1" + } + }, + "@graphql-tools/executor": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.5.0.tgz", + "integrity": "sha512-3HzAxfexmynEWwRB56t/BT+xYKEYLGPvJudR1jfs+XZX8bpfqujEhqVFoxmkpEE8BbFcKuBNoQyGkTi1eFJ+hA==", + "requires": { + "@graphql-tools/utils": "^10.11.0", + "@graphql-typed-document-node/core": "^3.2.0", + "@repeaterjs/repeater": "^3.0.4", + "@whatwg-node/disposablestack": "^0.0.6", + "@whatwg-node/promise-helpers": "^1.0.0", + "tslib": "^2.4.0" + } + }, + "@graphql-tools/executor-common": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-common/-/executor-common-1.0.5.tgz", + "integrity": "sha512-gsBRxP4ui8s7/ppKGCJUQ9xxTNoFpNYmEirgM52EHo74hL5hrpS5o4zOmBH33+9t2ZasBziIfupYtLNa0DgK0g==", + "requires": { + "@envelop/core": "^5.4.0", + "@graphql-tools/utils": "^10.10.3" + } + }, + "@graphql-tools/executor-http": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-3.0.7.tgz", + "integrity": "sha512-sHjtiUZmRtkjhpSzMhxT2ywAGzHjuB1rHsiaSLAq8U5BQg5WoLakKYD7BajgVHwNbfWEc+NnFiJI7ldyhiciiQ==", + "requires": { + "@graphql-hive/signal": "^2.0.0", + "@graphql-tools/executor-common": "^1.0.5", + "@graphql-tools/utils": "^10.10.3", + "@repeaterjs/repeater": "^3.0.4", + "@whatwg-node/disposablestack": "^0.0.6", + "@whatwg-node/fetch": "^0.10.13", + "@whatwg-node/promise-helpers": "^1.3.2", + "meros": "^1.3.2", + "tslib": "^2.8.1" + } + }, + "@graphql-tools/merge": { + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.6.tgz", + "integrity": "sha512-bTnP+4oom4nDjmkS3Ykbe+ljAp/RIiWP3R35COMmuucS24iQxGLa9Hn8VMkLIoaoPxgz6xk+dbC43jtkNsFoBw==", + "requires": { + "@graphql-tools/utils": "^10.11.0", + "tslib": "^2.4.0" + } + }, + "@graphql-tools/schema": { + "version": "10.0.30", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.30.tgz", + "integrity": "sha512-yPXU17uM/LR90t92yYQqn9mAJNOVZJc0nQtYeZyZeQZeQjwIGlTubvvoDL0fFVk+wZzs4YQOgds2NwSA4npodA==", + "requires": { + "@graphql-tools/merge": "^9.1.6", + "@graphql-tools/utils": "^10.11.0", + "tslib": "^2.4.0" + } + }, + "@graphql-tools/utils": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.11.0.tgz", + "integrity": "sha512-iBFR9GXIs0gCD+yc3hoNswViL1O5josI33dUqiNStFI/MHLCEPduasceAcazRH77YONKNiviHBV8f7OgcT4o2Q==", + "requires": { + "@graphql-typed-document-node/core": "^3.1.1", + "@whatwg-node/promise-helpers": "^1.0.0", + "cross-inspect": "1.0.1", + "tslib": "^2.4.0" + } + }, + "@graphql-tools/wrap": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-11.1.2.tgz", + "integrity": "sha512-TcKZzUzJNmuyMBQ1oMdnxhBUUacN/5VEJu0/1KVce2aIzCwTTaN9JTU3MgjO7l5Ixn4QLkc6XbxYNv0cHDQgtQ==", + "requires": { + "@graphql-tools/delegate": "^12.0.2", + "@graphql-tools/schema": "^10.0.29", + "@graphql-tools/utils": "^10.10.3", + "@whatwg-node/promise-helpers": "^1.3.2", + "tslib": "^2.8.1" + } + }, + "@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "requires": {} + }, "@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3896,6 +4521,11 @@ "dev": true, "optional": true }, + "@repeaterjs/repeater": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.6.tgz", + "integrity": "sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==" + }, "@spruceid/siwe-parser": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@spruceid/siwe-parser/-/siwe-parser-2.0.2.tgz", @@ -3962,7 +4592,7 @@ "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "devOptional": true, + "dev": true, "requires": { "@types/connect": "*", "@types/node": "*" @@ -3972,7 +4602,7 @@ "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "devOptional": true, + "dev": true, "requires": { "@types/node": "*" } @@ -3990,7 +4620,7 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "devOptional": true, + "dev": true, "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -4002,7 +4632,7 @@ "version": "4.17.41", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", - "devOptional": true, + "dev": true, "requires": { "@types/node": "*", "@types/qs": "*", @@ -4032,26 +4662,24 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "devOptional": true + "dev": true }, - "@types/http-proxy": { - "version": "1.17.14", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", - "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", - "requires": { - "@types/node": "*" - } + "@types/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==" }, "@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "devOptional": true + "dev": true }, "@types/node": { "version": "20.9.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "devOptional": true, "requires": { "undici-types": "~5.26.4" } @@ -4070,19 +4698,19 @@ "version": "6.9.10", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==", - "devOptional": true + "dev": true }, "@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "devOptional": true + "dev": true }, "@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "devOptional": true, + "dev": true, "requires": { "@types/mime": "^1", "@types/node": "*" @@ -4092,7 +4720,7 @@ "version": "1.15.5", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", - "devOptional": true, + "dev": true, "requires": { "@types/http-errors": "*", "@types/mime": "*", @@ -4108,6 +4736,48 @@ "@types/node": "*" } }, + "@types/yup": { + "version": "0.29.13", + "resolved": "https://registry.npmjs.org/@types/yup/-/yup-0.29.13.tgz", + "integrity": "sha512-qRyuv+P/1t1JK1rA+elmK1MmCL1BapEzKKfbEhDBV/LMMse4lmhZ/XbgETI39JveDJRpLjmToOI6uFtMW/WR2g==" + }, + "@whatwg-node/disposablestack": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@whatwg-node/disposablestack/-/disposablestack-0.0.6.tgz", + "integrity": "sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw==", + "requires": { + "@whatwg-node/promise-helpers": "^1.0.0", + "tslib": "^2.6.3" + } + }, + "@whatwg-node/fetch": { + "version": "0.10.13", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.13.tgz", + "integrity": "sha512-b4PhJ+zYj4357zwk4TTuF2nEe0vVtOrwdsrNo5hL+u1ojXNhh1FgJ6pg1jzDlwlT4oBdzfSwaBwMCtFCsIWg8Q==", + "requires": { + "@whatwg-node/node-fetch": "^0.8.3", + "urlpattern-polyfill": "^10.0.0" + } + }, + "@whatwg-node/node-fetch": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.8.4.tgz", + "integrity": "sha512-AlKLc57loGoyYlrzDbejB9EeR+pfdJdGzbYnkEuZaGekFboBwzfVYVMsy88PMriqPI1ORpiGYGgSSWpx7a2sDA==", + "requires": { + "@fastify/busboy": "^3.1.1", + "@whatwg-node/disposablestack": "^0.0.6", + "@whatwg-node/promise-helpers": "^1.3.2", + "tslib": "^2.6.3" + } + }, + "@whatwg-node/promise-helpers": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz", + "integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==", + "requires": { + "tslib": "^2.6.3" + } + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -4229,6 +4899,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -4344,6 +5015,14 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "cross-inspect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz", + "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==", + "requires": { + "tslib": "^2.4.0" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -4355,6 +5034,11 @@ "which": "^2.0.1" } }, + "dataloader": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz", + "integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -4503,11 +5187,6 @@ "@ethersproject/wordlists": "5.7.0" } }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, "express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -4626,6 +5305,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -4644,11 +5324,6 @@ "unpipe": "~1.0.0" } }, - "follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==" - }, "foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -4770,8 +5445,91 @@ "graphql": { "version": "16.8.1", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", - "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", - "dev": true + "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==" + }, + "graphql-middleware": { + "version": "6.1.35", + "resolved": "https://registry.npmjs.org/graphql-middleware/-/graphql-middleware-6.1.35.tgz", + "integrity": "sha512-azawK7ApUYtcuPGRGBR9vDZu795pRuaFhO5fgomdJppdfKRt7jwncuh0b7+D3i574/4B+16CNWgVpnGVlg3ZCg==", + "requires": { + "@graphql-tools/delegate": "^8.8.1", + "@graphql-tools/schema": "^8.5.1" + }, + "dependencies": { + "@graphql-tools/batch-execute": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-8.5.1.tgz", + "integrity": "sha512-hRVDduX0UDEneVyEWtc2nu5H2PxpfSfM/riUlgZvo/a/nG475uyehxR5cFGvTEPEQUKY3vGIlqvtRigzqTfCew==", + "requires": { + "@graphql-tools/utils": "8.9.0", + "dataloader": "2.1.0", + "tslib": "^2.4.0", + "value-or-promise": "1.0.11" + } + }, + "@graphql-tools/delegate": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-8.8.1.tgz", + "integrity": "sha512-NDcg3GEQmdEHlnF7QS8b4lM1PSF+DKeFcIlLEfZFBvVq84791UtJcDj8734sIHLukmyuAxXMfA1qLd2l4lZqzA==", + "requires": { + "@graphql-tools/batch-execute": "8.5.1", + "@graphql-tools/schema": "8.5.1", + "@graphql-tools/utils": "8.9.0", + "dataloader": "2.1.0", + "tslib": "~2.4.0", + "value-or-promise": "1.0.11" + } + }, + "@graphql-tools/merge": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.1.tgz", + "integrity": "sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg==", + "requires": { + "@graphql-tools/utils": "8.9.0", + "tslib": "^2.4.0" + } + }, + "@graphql-tools/schema": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-8.5.1.tgz", + "integrity": "sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg==", + "requires": { + "@graphql-tools/merge": "8.3.1", + "@graphql-tools/utils": "8.9.0", + "tslib": "^2.4.0", + "value-or-promise": "1.0.11" + } + }, + "@graphql-tools/utils": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.9.0.tgz", + "integrity": "sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg==", + "requires": { + "tslib": "^2.4.0" + } + }, + "dataloader": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.1.0.tgz", + "integrity": "sha512-qTcEYLen3r7ojZNgVUaRggOI+KM7jrKxXeSHhogh/TWxYMeONEMqY+hmkobiYQozsGIyg9OYVzO4ZIfoB4I0pQ==" + }, + "tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + } + } + }, + "graphql-shield": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/graphql-shield/-/graphql-shield-7.6.5.tgz", + "integrity": "sha512-etbzf7UIhQW6vadn/UR+ds0LJOceO8ITDXwbUkQMlP2KqPgSKTZRE2zci+AUfqP+cpV9zDQdbTJfPfW5OCEamg==", + "requires": { + "@types/yup": "0.29.13", + "object-hash": "^3.0.0", + "tslib": "^2.4.0", + "yup": "^0.32.0" + } }, "has-flag": { "version": "3.0.0", @@ -4838,28 +5596,6 @@ "toidentifier": "1.0.1" } }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", - "requires": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - } - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -4902,7 +5638,8 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -4914,6 +5651,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -4921,12 +5659,8 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, "isexe": { "version": "2.0.0", @@ -4956,6 +5690,16 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4987,6 +5731,12 @@ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, + "meros": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/meros/-/meros-1.3.2.tgz", + "integrity": "sha512-Q3mobPbvEx7XbwhnC1J1r60+5H6EZyNccdzSz0eGexJRwouUtTZxPVRGdqKtxlpD84ScK4+tIGldkqDtCKdI0A==", + "requires": {} + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -4996,6 +5746,7 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, "requires": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -5063,6 +5814,11 @@ "integrity": "sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==", "dev": true }, + "nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" + }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -5131,6 +5887,11 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + }, "object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -5192,7 +5953,8 @@ "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true }, "plimit-lit": { "version": "1.6.1", @@ -5203,6 +5965,11 @@ "queue-lit": "^1.5.1" } }, + "property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -5262,11 +6029,6 @@ "picomatch": "^2.2.1" } }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5522,6 +6284,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "requires": { "is-number": "^7.0.0" } @@ -5531,6 +6294,11 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, + "toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, "touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", @@ -5591,6 +6359,11 @@ "strip-bom": "^3.0.0" } }, + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -5601,9 +6374,9 @@ } }, "typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true }, "uid-safe": { @@ -5623,7 +6396,8 @@ "undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "devOptional": true }, "unpipe": { "version": "1.0.0", @@ -5638,6 +6412,11 @@ "punycode": "^2.1.0" } }, + "urlpattern-polyfill": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz", + "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==" + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -5654,6 +6433,11 @@ "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", "integrity": "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==" }, + "value-or-promise": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz", + "integrity": "sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -5764,6 +6548,20 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true + }, + "yup": { + "version": "0.32.11", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", + "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", + "requires": { + "@babel/runtime": "^7.15.4", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + } } } } diff --git a/package.json b/package.json index 5054dec..91f8ec4 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,13 @@ "main": "build/index.js", "dependencies": { "@colony/core": "^2.0.1", + "@graphql-tools/executor-http": "^3.0.7", + "@graphql-tools/wrap": "^11.1.2", "cors": "^2.8.5", "express": "^4.18.2", "express-session": "^1.17.3", - "http-proxy-middleware": "^2.0.6", + "graphql-middleware": "^6.1.35", + "graphql-shield": "^7.6.5", "node-fetch": "2.6", "siwe": "^2.1.4", "ws": "^8.16.0" @@ -27,7 +30,7 @@ "ts-node": "^10.9.1", "tsc-alias": "^1.8.8", "tsconfig-paths": "^4.2.0", - "typescript": "^5.2.2" + "typescript": "^5.9.3" }, "scripts": { "dev": "NODE_ENV=dev nodemon", diff --git a/src/RequestError.ts b/src/RequestError.ts deleted file mode 100644 index 72084f0..0000000 --- a/src/RequestError.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Response, ResponseTypes } from '~types'; - -export class RequestError extends Error { - response: Response; - - constructor(message: string, data: string | number | boolean | string[] | number[] | boolean[] = '') { - super(message); - this.name = 'RequestError'; - this.response = { - message, - type: ResponseTypes.Error, - data, - }; - } -} diff --git a/src/helpers.ts b/src/helpers.ts index 3e7de80..68ecfb8 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,14 +1,14 @@ -import { parse } from 'graphql'; -import dotenv from "dotenv"; +import dotenv from 'dotenv'; import { default as fetch, Request as NodeFetchRequst } from 'node-fetch'; -import { Response as ExpressResponse, Request } from 'express-serve-static-core'; -import { RequestError } from './RequestError'; import { - OperationTypes, + Response as ExpressResponse, + Request, +} from 'express-serve-static-core'; +import { StaticOriginCallback, HttpStatuses, - Response, + ApiResponse, Headers, ContentTypes, ServerMethods, @@ -20,58 +20,21 @@ const BLOCK_TIME = Number(process.env.DEFAULT_BLOCK_TIME) * 1000 || 5000; export const isDevMode = (): boolean => process.env.NODE_ENV !== 'prod'; -export const detectOperation = (body: Record): { - operationType: OperationTypes, - operations: string[], - variables?: string, -} => { - let isMutation = false; - - if (!body) { - throw new RequestError('no body'); - } - if (!body?.query) { - throw new RequestError('graphql request malformed'); - } - - if (JSON.stringify(body).includes(OperationTypes.Mutation)) { - isMutation = true; - } - - let parsedQuery: any; - try { - parsedQuery = parse(body.query); - } catch (error) { - // silent - } - - if (!parsedQuery) { - throw new RequestError('graphql request malformed'); - } - - const [{ operation: operationType }] = parsedQuery.definitions || [{}]; - if (operationType === OperationTypes.Mutation) { - isMutation = true; - } - - const operationNames = parsedQuery.definitions[0].selectionSet.selections.map( - (selection: any) => selection.name.value, - ); - - return { - operationType: isMutation ? OperationTypes.Mutation : OperationTypes.Query, - operations: operationNames, - variables: body.variables ? JSON.stringify(body.variables) : undefined, - }; -}; - -export const getStaticOrigin = (origin?: string, callback?: StaticOriginCallback): string | undefined => { +export const getStaticOrigin = ( + origin?: string, + callback?: StaticOriginCallback, +): string | undefined => { let isAllowedOrigin = false; if (isDevMode()) { - if (origin?.includes('http://localhost') || origin?.includes('https://localhost') || origin?.includes('http://127') || origin?.includes('https://127')) { + if ( + origin?.includes('http://localhost') || + origin?.includes('https://localhost') || + origin?.includes('http://127') || + origin?.includes('https://127') + ) { isAllowedOrigin = true; } - }; + } if (origin === process.env.ORIGIN_URL) { isAllowedOrigin = true; } @@ -84,23 +47,27 @@ export const getStaticOrigin = (origin?: string, callback?: StaticOriginCallback export const sendResponse = ( response: ExpressResponse, request: Request, - message?: Response, + message?: ApiResponse, status: HttpStatuses = HttpStatuses.OK, -) => response.set({ - [Headers.AllowOrigin]: getStaticOrigin(request.headers.origin), - [Headers.ContentType]: ContentTypes.Json, - [Headers.PoweredBy]: 'Colony', -}).status(status).json(message); +) => + response + .set({ + [Headers.AllowOrigin]: getStaticOrigin(request.headers.origin), + [Headers.ContentType]: ContentTypes.Json, + [Headers.PoweredBy]: 'Colony', + }) + .status(status) + .json(message); export const getRemoteIpAddress = (request: Request): string => typeof request.headers[Headers.ForwardedFor] === 'string' ? request.headers[Headers.ForwardedFor] : request.headers[Headers.ForwardedFor]?.join(';') || - request.ip || - request.ips.join(';') || - request.connection.remoteAddress || - request.socket.remoteAddress || - ''; + request.ip || + request.ips.join(';') || + request.connection.remoteAddress || + request.socket.remoteAddress || + ''; export const resetSession = (request: Request): void => { request.session.auth = undefined; @@ -117,14 +84,21 @@ export const logger = (...args: any[]): void => { export const graphqlRequest = async ( queryOrMutation: string, - variables?: Record + variables?: Record, + walletAddress?: string, ) => { + const headers: Record = { + [Headers.ApiKey]: process.env.APPSYNC_API_KEY || '', + [Headers.ContentType]: ContentTypes.Json, + }; + + if (walletAddress) { + headers[Headers.WalletAddress] = walletAddress; + } + const options = { method: ServerMethods.Post.toUpperCase(), - headers: { - [Headers.ApiKey]: process.env.APPSYNC_API_KEY || '', - [Headers.ContentType]: ContentTypes.Json, - }, + headers, body: JSON.stringify({ query: queryOrMutation, variables, @@ -133,12 +107,9 @@ export const graphqlRequest = async ( const request = new NodeFetchRequst(process.env.APPSYNC_API || '', options); - let body; - let response; - try { - response = await fetch(request); - body = await response.json(); + const response = await fetch(request); + const body = await response.json(); return body; } catch (error) { /* @@ -149,37 +120,26 @@ export const graphqlRequest = async ( } }; -export const delay = async (timeout: number) => { - return new Promise(resolve => { - setTimeout(resolve, timeout); - }); -} +const MAX_RETRIES = 3; -export const tryFetchGraphqlQuery = async ( - queryOrMutation: string, - variables?: Record, - maxRetries: number = 3, - blockTime: number = BLOCK_TIME -) => { - let currentTry = 0; - while (true) { - const result = await graphqlRequest(queryOrMutation, variables); +const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - /* - * @NOTE That this limits to only fetching one operation at a time - */ +export const fetchWithRetry = async ( + query: string, + variables: Record, +): Promise => { + for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) { + const result = await graphqlRequest(query, variables); if (result?.data) { - const { data } = result; - if (data[Object.keys(data)[0]]) { - return data[Object.keys(data)[0]]; + const data = result.data; + const value = data[Object.keys(data)[0]]; + if (value) { + return value as T; } } - - if (currentTry < maxRetries) { - await delay(blockTime); - currentTry += 1; - } else { - throw new Error('Could not fetch graphql data in time'); + if (attempt < MAX_RETRIES) { + await delay(BLOCK_TIME); } } -} + return null; +}; diff --git a/src/index.ts b/src/index.ts index 730096b..7934533 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,19 +1,31 @@ -import dotenv from "dotenv"; +import dotenv from 'dotenv'; import proxyServerInstance from './server'; import { handleWsUpgrade } from './routes/graphql/ws'; +import { initSchema } from './schema'; dotenv.config(); const port = process.env.DEFAULT_PORT || 3005; -const proxy = proxyServerInstance(); -const server = proxy.listen(port, () => { - /* - * @NOTE Use console log here as to ensure it will always be logged - */ - console.log(`Authentication proxy listening on port ${port}`); -}); +(async () => { + try { + await initSchema(); + console.log('GraphQL schema loaded successfully'); + } catch (error) { + console.error('Failed to load GraphQL schema:', error); + process.exit(1); + } -// Custom websocker upgrade proxy handler -server.on('upgrade', handleWsUpgrade) + const proxy = proxyServerInstance(); + + const server = proxy.listen(port, () => { + /* + * @NOTE Use console log here as to ensure it will always be logged + */ + console.log(`Authentication proxy listening on port ${port}`); + }); + + // Custom websocker upgrade proxy handler + server.on('upgrade', handleWsUpgrade); +})(); diff --git a/src/permissions.ts b/src/permissions.ts new file mode 100644 index 0000000..af400d2 --- /dev/null +++ b/src/permissions.ts @@ -0,0 +1,343 @@ +import { shield, rule, allow, deny, or } from 'graphql-shield'; +import { Path } from 'graphql/jsutils/Path'; +import { FieldNode, GraphQLResolveInfo, ValueNode } from 'graphql'; +import { ColonyRole, Id } from '@colony/core'; + +import { fetchWithRetry } from './helpers'; +import { + getAllColonyRoles, + getColonyAction, + getColonyRole, + getColonyTokens, + getStreamingPayment, + getTransaction, +} from './queries'; +import { UserRole } from '~types'; + +const getPathArray = (path: Path | undefined): (string | number)[] => { + const segments: (string | number)[] = []; + let current = path; + while (current) { + segments.unshift(current.key); + current = current.prev; + } + return segments; +}; + +const getRootFieldNode = ( + info: GraphQLResolveInfo, + rootField: string | number, +): FieldNode | undefined => { + for (const selection of info.operation.selectionSet.selections) { + if (selection.kind === 'Field' && selection.name.value === rootField) { + return selection; + } + } + return undefined; +}; + +const resolveValue = ( + node: ValueNode, + variables: Record, +): unknown => { + switch (node.kind) { + case 'Variable': + return variables[node.name.value]; + case 'IntValue': + return parseInt(node.value, 10); + case 'FloatValue': + return parseFloat(node.value); + case 'StringValue': + case 'BooleanValue': + case 'EnumValue': + return node.value; + case 'NullValue': + return null; + case 'ListValue': + return node.values.map((v) => resolveValue(v, variables)); + case 'ObjectValue': + return Object.fromEntries( + node.fields.map((f) => [ + f.name.value, + resolveValue(f.value, variables), + ]), + ); + } +}; + +const getRootFieldArgs = ( + info: GraphQLResolveInfo, + rootFieldNode: FieldNode, +): Record => { + const args: Record = {}; + for (const arg of rootFieldNode.arguments ?? []) { + args[arg.name.value] = resolveValue(arg.value, info.variableValues); + } + return args; +}; + +const getByPath = (obj: Record, path: string): unknown => { + return path.split('.').reduce((acc, key) => { + if (acc && typeof acc === 'object') { + return (acc as Record)[key]; + } + return undefined; + }, obj); +}; + +const isAuthenticated = rule()((_parent, _args, ctx) => { + return Boolean(ctx.userAddress); +}); + +const matchesUserAddress = (path: string) => + rule()((_parent, args, ctx) => { + if (!ctx.userAddress) { + return false; + } + const value = getByPath(args, path); + return String(value).toLowerCase() === ctx.userAddress.toLowerCase(); + }); + +const ownsTransaction = rule()(async (_parent, args, ctx) => { + if (!ctx.userAddress) { + return false; + } + const { id } = args.input as { id: string }; + const transaction = await fetchWithRetry<{ from: string }>(getTransaction, { + transactionId: id, + }); + return transaction?.from?.toLowerCase() === ctx.userAddress.toLowerCase(); +}); + +const isOwnContributor = rule()((_parent, args, ctx) => { + if (!ctx.userAddress) { + return false; + } + const { id } = args.input as { id: string }; + const [, contributorAddress] = id.split('_'); + return contributorAddress?.toLowerCase() === ctx.userAddress.toLowerCase(); +}); + +const hasColonyRole = (colonyAddressPath: string, role: ColonyRole) => + rule()(async (_parent, args, ctx) => { + if (!ctx.userAddress) { + return false; + } + const colonyAddress = getByPath(args, colonyAddressPath); + if (!colonyAddress) { + return false; + } + const combinedId = `${colonyAddress}_1_${ctx.userAddress}_roles`; + const data = await fetchWithRetry(getColonyRole, { + combinedId, + }); + return !!data?.[`role_${role}` as keyof UserRole]; + }); + +const canDeleteColonyTokens = rule()(async (_parent, args, ctx) => { + if (!ctx.userAddress) { + return false; + } + const { id } = args.input as { id: string }; + const tokenData = await fetchWithRetry<{ colonyID: string }>( + getColonyTokens, + { tokenColonyId: id }, + ); + if (!tokenData?.colonyID) { + return false; + } + const combinedId = `${tokenData.colonyID}_1_${ctx.userAddress}_roles`; + const data = await fetchWithRetry(getColonyRole, { combinedId }); + return !!data?.[`role_${ColonyRole.Root}`]; +}); + +const isActionInitiator = rule()(async (_parent, args, ctx) => { + if (!ctx.userAddress) { + return false; + } + const { id } = args.input as { id: string }; + const action = await fetchWithRetry<{ initiatorAddress: string }>( + getColonyAction, + { actionId: id }, + ); + return ( + action?.initiatorAddress?.toLowerCase() === ctx.userAddress.toLowerCase() + ); +}); + +const canUpdateStreamingPaymentMetadata = rule()(async (_parent, args, ctx) => { + if (!ctx.userAddress) { + return false; + } + const { id: streamingPaymentId } = args.input as { id: string }; + const streamingPayment = await fetchWithRetry<{ nativeDomainId: number }>( + getStreamingPayment, + { streamingPaymentId }, + ); + if (!streamingPayment) { + return false; + } + const [colonyAddress] = streamingPaymentId.split('_'); + const rolesData = await fetchWithRetry<{ items: UserRole[] }>( + getAllColonyRoles, + { targetAddress: ctx.userAddress, colonyAddress }, + ); + if (!rolesData?.items) { + return false; + } + return rolesData.items.some((item) => { + const [, roleDomainId] = item.id.split('_'); + const matchesDomain = + roleDomainId === String(streamingPayment.nativeDomainId) || + roleDomainId === String(Id.RootDomain); + const hasRole = !!item[`role_${ColonyRole.Administration}`]; + return matchesDomain && hasRole; + }); +}); + +const isOwnUser = rule()((_parent, _args, ctx, info) => { + if (!ctx.userAddress) { + return false; + } + + const pathArray = getPathArray(info.path); + const rootField = pathArray[0]; + const rootFieldNode = getRootFieldNode(info, rootField); + const rootArgs = rootFieldNode ? getRootFieldArgs(info, rootFieldNode) : {}; + + if (rootField === 'getUserByAddress') { + return String(rootArgs.id).toLowerCase() === ctx.userAddress.toLowerCase(); + } + + return false; +}); + +const isUpdatingOwnProfile = rule()((_parent, _args, ctx, info) => { + if (!ctx.userAddress) { + return false; + } + + const pathArray = getPathArray(info.path); + const rootField = pathArray[0]; + const rootFieldNode = getRootFieldNode(info, rootField); + const rootArgs = rootFieldNode ? getRootFieldArgs(info, rootFieldNode) : {}; + + if (rootField === 'updateProfile') { + const input = rootArgs.input as { id: string }; + return input.id.toLowerCase() === ctx.userAddress.toLowerCase(); + } + + return false; +}); + +export const permissions = shield( + { + Query: { + '*': deny, + + bridgeCheckKYC: isAuthenticated, + bridgeGetDrainsHistory: isAuthenticated, + bridgeGetUserLiquidationAddress: isAuthenticated, + getProfileByEmail: isAuthenticated, + getUserNotificationsHMAC: isAuthenticated, + + bridgeGetGatewayFee: allow, + cacheTotalBalanceByColonyAddress: allow, + getActionsByColony: allow, + getColoniesByNativeTokenId: allow, + getColony: allow, + getColonyAction: allow, + getColonyActionByMotionId: allow, + getColonyByAddress: allow, + getColonyByName: allow, + getColonyByType: allow, + getColonyContributor: allow, + getColonyDecisionByColonyAddress: allow, + getColonyHistoricRole: allow, + getColonyMemberInvite: allow, + getColonyMotion: allow, + getContributorsByAddress: allow, + getCurrentVersionByKey: allow, + getDomainBalance: allow, + getExpenditure: allow, + getExtensionByColonyAndHash: allow, + getExtensionInstallationsCount: allow, + getMotionByTransactionHash: allow, + getMotionState: allow, + getMotionTimeoutPeriods: allow, + getPrivateBetaInviteCode: allow, + getProfileByUsername: allow, + getReputationMiningCycleMetadata: allow, + getRoleByDomainAndColony: allow, + getSafeTransactionStatus: allow, + getTokenByAddress: allow, + getTokenFromEverywhere: allow, + getTransaction: allow, + getTransactionsByUser: allow, + getTransactionsByUserAndGroup: allow, + getUserByAddress: allow, + getUserByLiquidationAddress: allow, + getUserReputation: allow, + getUserStakes: allow, + getUserTokenBalance: allow, + getVoterRewards: allow, + listCurrentNetworkInverseFees: allow, + listCurrentVersions: allow, + listTokens: allow, + listUsers: allow, + searchColonyActions: allow, + searchColonyContributors: allow, + }, + Mutation: { + '*': deny, + + validateUserInvite: allow, + updateContributorsWithReputation: allow, + + updateProfile: matchesUserAddress('input.id'), + createUniqueUser: matchesUserAddress('input.id'), + createUserNotificationsData: matchesUserAddress('input.id'), + updateNotificationsData: matchesUserAddress('input.userAddress'), + createTransaction: matchesUserAddress('input.from'), + updateTransaction: ownsTransaction, + createUserTokens: matchesUserAddress('input.userID'), + createColonyContributor: matchesUserAddress('input.contributorAddress'), + updateColonyContributor: isOwnContributor, + createColonyEtherealMetadata: matchesUserAddress( + 'input.initiatorAddress', + ), + updateColonyMetadata: hasColonyRole('input.id', ColonyRole.Root), + createColonyTokens: hasColonyRole('input.colonyID', ColonyRole.Root), + deleteColonyTokens: canDeleteColonyTokens, + createColonyActionMetadata: isActionInitiator, + initializeUser: isAuthenticated, + createColonyMetadata: isAuthenticated, + createDomainMetadata: isAuthenticated, + updateDomainMetadata: isAuthenticated, + createExpenditureMetadata: isAuthenticated, + createStreamingPaymentMetadata: isAuthenticated, + updateStreamingPaymentMetadata: canUpdateStreamingPaymentMetadata, + createAnnotation: isAuthenticated, + createColonyDecision: isAuthenticated, + bridgeXYZMutation: isAuthenticated, + bridgeCreateBankAccount: isAuthenticated, + bridgeUpdateBankAccount: isAuthenticated, + }, + Profile: { + email: or(isOwnUser, isUpdatingOwnProfile), + }, + User: { + bridgeCustomerId: isOwnUser, + privateBetaInviteCode: isOwnUser, + userPrivateBetaInviteCodeId: deny, + }, + Colony: { + colonyMemberInvite: deny, + colonyMemberInviteCode: deny, + }, + }, + { + fallbackRule: allow, + allowExternalErrors: true, + }, +); diff --git a/src/routes/graphql/graphql.ts b/src/routes/graphql/graphql.ts index 93ed753..aaa2774 100644 --- a/src/routes/graphql/graphql.ts +++ b/src/routes/graphql/graphql.ts @@ -1,201 +1,87 @@ -import dotenv from 'dotenv'; -import { fixRequestBody, Options, RequestHandler } from 'http-proxy-middleware'; -import { Response, Request, NextFunction } from 'express-serve-static-core'; -import { ClientRequest, IncomingMessage } from 'http'; +import { Response, Request } from 'express-serve-static-core'; +import { graphql } from 'graphql'; -import { - getStaticOrigin, - sendResponse, - getRemoteIpAddress, - logger, - detectOperation, -} from '~helpers'; -import { - ResponseTypes, - HttpStatuses, - ContentTypes, - Headers, - OperationTypes, - Urls, - ServerMethods, -} from '~types'; +import { getStaticOrigin, getRemoteIpAddress, logger } from '~helpers'; +import { HttpStatuses, ContentTypes, Headers, ResponseTypes } from '~types'; +import { getSchema } from '../../schema'; -import addressCanExecuteMutation from './mutations'; -import addressCanExecuteQuery from './queries'; +export const handleGraphQL = async (request: Request, response: Response) => { + const userAddress = request.session.auth?.address; + const userAuthenticated = !!request.session.auth; + const requestRemoteAddress = getRemoteIpAddress(request); -dotenv.config(); + const schema = getSchema(); -export const operationExecutionHandler: RequestHandler = async ( - request: Request, - response: Response, - nextFn: NextFunction, -) => { - // short circut early - if ( - request.path !== Urls.GraphQL || - request.method !== ServerMethods.Post.toUpperCase() - ) { - return nextFn(); - } - - const userAddress = request.session.auth?.address || ''; - const requestRemoteAddress = getRemoteIpAddress(request); + const { query, variables, operationName } = request.body; try { - response.locals.canExecuteMutation = await addressCanExecuteMutation( - request, + const result = await graphql({ + schema, + source: query, + variableValues: variables, + operationName, + contextValue: { + userAddress, + }, + }); + + const hasErrors = result.errors && result.errors.length > 0; + const hasPermissionError = result.errors?.some( + (error) => error.message === 'Not Authorised!', ); - response.locals.canExecuteQuery = await addressCanExecuteQuery(request); - return nextFn(); - } catch (error: any) { + logger( - `${ - userAddress ? `auth-${userAddress}` : 'non-auth' - } request malformed graphql ${ - request.body ? JSON.stringify(request.body) : '' - } from ${requestRemoteAddress}`, + `${userAuthenticated ? 'auth' : 'non-auth'} request${ + userAddress ? ` from ${userAddress}` : '' + } at ${requestRemoteAddress} ${ + hasPermissionError + ? '\x1b[31m FORBIDDEN \x1b[0m' + : hasErrors + ? '\x1b[31m ERROR \x1b[0m' + : '\x1b[32m OK \x1b[0m' + }`, ); - return sendResponse(response, request, error, HttpStatuses.SERVER_ERROR); - } -}; - -export const graphQlProxyRouteHandler: Options = { - target: process.env.APPSYNC_API, - changeOrigin: true, - headers: { - [Headers.ApiKey]: process.env.APPSYNC_API_KEY || '', - [Headers.ContentType]: ContentTypes.Json, - }, - pathRewrite: { '^/graphql': '' }, - onProxyReq: ( - proxyRequest: ClientRequest, - request: Request, - response: Response, - ) => { - const userAuthenticated = !!request.session.auth; - const userAddress = request.session.auth?.address || ''; - const requestRemoteAddress = getRemoteIpAddress(request); - try { - if (request?.body?.query) { - /* - * Used for UI only, the real magic with detection happens in operationExecutionHandler - */ - const { operationType, operations, variables } = detectOperation( - request.body, - ); - - /* - * Mutations need to be handled on a case by case basis - * Some are allowed without auth (cache refresh ones) - * Others based on if the user has the appropriate address and/or role - */ - const canExecuteMutation = - operationType === OperationTypes.Mutation && - response.locals.canExecuteMutation; - /* - * By default, all queries are allowed - * However, some will not execute correctly if a user address is not provided - */ - const canExecuteQuery = - operationType === OperationTypes.Query && - response.locals.canExecuteQuery; - - const canExecute = canExecuteMutation || canExecuteQuery; - - logger( - `${ - userAuthenticated ? `auth` : 'non-auth' - } ${operationType} ${operations} ${JSON.stringify(variables).slice( - 0, - 500, - )}${ - JSON.stringify(variables).length > 499 - ? ` [+${JSON.stringify(variables).length - 499} chars more]` - : '' - }${ - userAddress ? ` from ${userAddress}` : '' - } at ${requestRemoteAddress} was ${ - canExecute - ? '\x1b[32m ALLOWED \x1b[0m' - : '\x1b[31m FORBIDDEN \x1b[0m' - }`, - ); - - // allowed - if (canExecute) { - proxyRequest.setHeader(Headers.WalletAddress, userAddress); - return fixRequestBody(proxyRequest, request); - } - - // forbidden - return sendResponse( - response, - request, - { - message: 'forbidden', - type: ResponseTypes.Auth, - data: '', - }, - HttpStatuses.FORBIDDEN, - ); - } - - /* - * Malformed request - */ - logger( - `${userAuthenticated ? `auth` : 'non-auth'} request malformed graphql ${ - request.body ? JSON.stringify(request.body) : '' - }${ - userAddress ? ` from ${userAddress}` : '' - } at ${requestRemoteAddress}`, - ); - return sendResponse( - response, - request, - { - message: 'malformed graphql request', - type: ResponseTypes.Error, + if (hasPermissionError) { + return response + .set({ + [Headers.AllowOrigin]: getStaticOrigin(request.headers.origin), + [Headers.ContentType]: ContentTypes.Json, + [Headers.PoweredBy]: 'Colony', + }) + .status(HttpStatuses.FORBIDDEN) + .json({ + message: 'forbidden', + type: ResponseTypes.Auth, data: '', - }, - HttpStatuses.SERVER_ERROR, - ); - } catch (error: any) { - /* - * GraphQL error (comes from the AppSync endopoint) - */ - logger( - `${userAuthenticated ? `auth` : 'non-auth'} graphql proxy error ${ - error?.message - } ${request.body ? JSON.stringify(request.body) : ''}${ - userAddress ? ` from ${userAddress}` : '' - } at ${requestRemoteAddress}`, - ); - return sendResponse( - response, - request, - { - message: 'graphql error', - type: ResponseTypes.Error, - data: error?.message || '', - }, - HttpStatuses.SERVER_ERROR, - ); + }); } - }, - // selfHandleResponse: true, - onProxyRes: (proxyResponse: IncomingMessage, request: Request) => { - proxyResponse.headers[Headers.AllowOrigin] = getStaticOrigin( - request.headers.origin, + + return response + .set({ + [Headers.AllowOrigin]: getStaticOrigin(request.headers.origin), + [Headers.ContentType]: ContentTypes.Json, + [Headers.PoweredBy]: 'Colony', + }) + .status(HttpStatuses.OK) + .json(result); + } catch (error: unknown) { + const message = + error instanceof Error ? error.message : 'GraphQL execution error'; + + logger( + `${userAuthenticated ? 'auth' : 'non-auth'} request${ + userAddress ? ` from ${userAddress}` : '' + } at ${requestRemoteAddress} \x1b[31m EXCEPTION \x1b[0m: ${message}`, ); - proxyResponse.headers[Headers.PoweredBy] = 'Colony'; - }, - logProvider: () => ({ - log: logger, - info: logger, - error: logger, - warn: logger, - debug: logger, - }), + + return response + .set({ + [Headers.AllowOrigin]: getStaticOrigin(request.headers.origin), + [Headers.ContentType]: ContentTypes.Json, + [Headers.PoweredBy]: 'Colony', + }) + .status(HttpStatuses.SERVER_ERROR) + .json({ errors: [{ message }] }); + } }; diff --git a/src/routes/graphql/mutations.ts b/src/routes/graphql/mutations.ts deleted file mode 100644 index cde12bd..0000000 --- a/src/routes/graphql/mutations.ts +++ /dev/null @@ -1,315 +0,0 @@ -import { Request } from 'express-serve-static-core'; -import { ColonyRole, Id } from '@colony/core'; - -import { logger, detectOperation, tryFetchGraphqlQuery } from '~helpers'; -import { MutationOperations, UserRole } from '~types'; -import { - getAllColonyRoles, - getColonyAction, - getColonyRole, - getColonyTokens, - getStreamingPayment, - getTransaction, -} from '~queries'; - -const hasMutationPermissions = async ( - operationName: string, - request: Request, -): Promise => { - const userAddress = request.session.auth?.address; - const { variables = '{}' } = detectOperation(request.body); - - try { - switch (operationName) { - /* - * Users - */ - case MutationOperations.CreateUniqueUser: - case MutationOperations.UpdateUserProfile: - case MutationOperations.CreateUserNotificationsData: { - const { - input: { id }, - } = JSON.parse(variables); - return userAddress && id && id.toLowerCase() === userAddress.toLowerCase(); - } - case MutationOperations.InitializeUser: { - // This is always allowed as the actual check is happening in the lambda - return true; - } - case MutationOperations.UpdateUserNotificationsData: { - const { - input: { userAddress: mutationUserAddress }, - } = JSON.parse(variables); - - return ( - userAddress?.toLowerCase() === mutationUserAddress?.toLowerCase() - ); - } - case MutationOperations.CreateTransaction: { - const { - input: { from }, - } = JSON.parse(variables); - return userAddress && from && from.toLowerCase() === userAddress.toLowerCase(); - } - case MutationOperations.UpdateTransaction: { - const { - input: { id, from }, - } = JSON.parse(variables); - - try { - const data = await tryFetchGraphqlQuery(getTransaction, { - transactionId: id, - }); - - // A user should only be allowed to update transactions made by them. - return ( - from && userAddress && - from.toLowerCase() === userAddress.toLowerCase() && // The logged in user is the same as the "from" in the mutation - data.from && data.from.toLowerCase() === userAddress.toLowerCase() // The logged in user is the same as the "from" in the fetched transaction - ); - } catch (error) { - // silent - return false; - } - } - case MutationOperations.CreateUserTokens: { - const { - input: { userID }, - } = JSON.parse(variables); - return userID && userAddress && userID.toLowerCase() === userAddress.toLowerCase(); - } - /* - * Colony - */ - case MutationOperations.UpdateColonyMetadata: { - const { - input: { id: colonyAddress }, - } = JSON.parse(variables); - if (!userAddress) { - return false; - } - try { - const data = await tryFetchGraphqlQuery(getColonyRole, { - combinedId: `${colonyAddress}_1_${userAddress}_roles`, - }); - return !!data[`role_${ColonyRole.Root}`]; - } catch (error) { - // silent - return false; - } - } - case MutationOperations.CreateColonyContributor: { - const { - input: { contributorAddress }, - } = JSON.parse(variables); - return contributorAddress && userAddress && contributorAddress.toLowerCase() === userAddress.toLowerCase(); - } - case MutationOperations.UpdateColonyContributor: { - const { - input: { id: combinedContributorId }, - } = JSON.parse(variables); - const [, contributorWalletAddress] = combinedContributorId.split('_'); - return ( - contributorWalletAddress && userAddress && - contributorWalletAddress.toLowerCase() === userAddress.toLowerCase() - ); - } - case MutationOperations.CreateColonyEtherealMetadata: { - const { - input: { initiatorAddress }, - } = JSON.parse(variables); - return initiatorAddress && userAddress && initiatorAddress?.toLowerCase() === userAddress?.toLowerCase(); - } - /* - * Domains - */ - case MutationOperations.CreateDomain: { - const { - input: { colonyId: colonyAddress }, - } = JSON.parse(variables); - try { - if (!userAddress) { - return false; - } - const data = await tryFetchGraphqlQuery(getColonyRole, { - combinedId: `${colonyAddress}_1_${userAddress}_roles`, - }); - return !!data[`role_${ColonyRole.Architecture}`]; - } catch (error) { - // silent - return false; - } - } - case MutationOperations.UpdateDomainMetadata: { - return true; - } - /* - * Actions, Mutations - */ - case MutationOperations.CreateColonyActionMetadata: - case MutationOperations.UpdateColonyAction: { - const { - input: { id: actionId }, - } = JSON.parse(variables); - try { - const data = await tryFetchGraphqlQuery(getColonyAction, { - actionId, - }); - return ( - data.initiatorAddress && userAddress && - data.initiatorAddress.toLowerCase() === userAddress.toLowerCase() - ); - } catch (error) { - // silent - return false; - } - } - /* - * Tokens - */ - case MutationOperations.CreateColonyTokens: { - const { - input: { colonyID: colonyAddress }, - } = JSON.parse(variables); - try { - if (!userAddress) { - return false; - } - const data = await tryFetchGraphqlQuery(getColonyRole, { - combinedId: `${colonyAddress}_1_${userAddress}_roles`, - }); - return !!data[`role_${ColonyRole.Root}`]; - } catch (error) { - // silent - return false; - } - } - case MutationOperations.DeleteColonyTokens: { - const { - input: { id: tokenColonyId }, - } = JSON.parse(variables); - try { - if (!userAddress) { - return false; - } - const tokenData = await tryFetchGraphqlQuery(getColonyTokens, { - tokenColonyId, - }); - - if (tokenData?.colonyID) { - const data = await tryFetchGraphqlQuery(getColonyRole, { - combinedId: `${tokenData.colonyID}_1_${userAddress}_roles`, - }); - return !!data[`role_${ColonyRole.Root}`]; - } - return false; - } catch (error) { - // silent - return false; - } - } - /** - * Expenditures - */ - case MutationOperations.CreateExpenditureMetadata: - case MutationOperations.CreateStreamingPaymentMetadata: - case MutationOperations.CreateAnnotation: { - return true; - } - case MutationOperations.UpdateStreamingPaymentMetadata: { - const { - input: { id: streamingPaymentId }, - } = JSON.parse(variables); - try { - // We need to check if the user has permissions in the domain the streaming payment was created in or the root domain - const { nativeDomainId } = await tryFetchGraphqlQuery( - getStreamingPayment, - { - streamingPaymentId, - }, - ); - const [colonyAddress] = streamingPaymentId.split('_'); - - const { items: userRoles }: { items: UserRole[] } = - await tryFetchGraphqlQuery(getAllColonyRoles, { - targetAddress: userAddress, - colonyAddress, - }); - - return userRoles.some((item) => { - const [, roleDomainId] = item.id.split('_'); - const matchesDomain = - roleDomainId === String(nativeDomainId) || - roleDomainId === String(Id.RootDomain); - const hasRole = !!item[`role_${ColonyRole.Administration}`]; - return matchesDomain && hasRole; - }); - } catch (error) { - // silent - return false; - } - } - /** - * Metadata can be created as part of the motion process, so we need to allow - * those mutations even for users with no permissions - */ - case MutationOperations.CreateColonyMetadata: - case MutationOperations.CreateDomainMetadata: - /* - * Always allow, it's just updating cache, anybody can trigger it - */ - case MutationOperations.CreateColonyDecision: - case MutationOperations.ValidateUserInvite: - case MutationOperations.GetTokenFromEverywhere: - case MutationOperations.UpdateContributorsWithReputation: { - return true; - } - /* - * Bridge XYZ mutation, always allow - */ - case MutationOperations.BridgeXYZMutation: - case MutationOperations.BridgeCreateBankAccount: - case MutationOperations.BridgeUpdateBankAccount: { - return true; - } - default: { - return false; - } - } - } catch (error) { - logger( - `Error when attempting to check if user ${userAddress} can execute mutation ${operationName} with variables ${variables}`, - error, - ); - /* - * If anything fails just prevent the mutation from executing - */ - return false; - } -}; - -const addressCanExecuteMutation = async ( - request: Request, -): Promise => { - try { - const { operations } = detectOperation(request.body); - - if (!operations.length) { - return false; - } - const canExecuteAllOperations = await Promise.all( - operations.map( - async (operationName) => - await hasMutationPermissions(operationName, request), - ), - ); - return canExecuteAllOperations.every((canExecute) => canExecute); - } catch (error) { - /* - * If anything fails just prevent the mutation from executing - */ - return false; - } -}; - -export default addressCanExecuteMutation; diff --git a/src/routes/graphql/queries.ts b/src/routes/graphql/queries.ts deleted file mode 100644 index b23869f..0000000 --- a/src/routes/graphql/queries.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Request } from 'express-serve-static-core'; - -import { logger, detectOperation } from '~helpers'; -import { QueryOperations } from '~types'; - -const hasQueryPermissions = async ( - operationName: string, - request: Request, -): Promise => { - const userAddress = request.session.auth?.address; - const { variables = '{}' } = detectOperation(request.body); - - try { - switch (operationName) { - /* - * GetUserNotificationsHMAC will fail if no userAddress is provided - */ - case QueryOperations.GetUserNotificationsHMAC: { - if (!userAddress) { - return false; - } - - return true; - } - default: { - // By default all queries are permitted - return true; - } - } - } catch (error) { - logger( - `Error when attempting to check if user ${userAddress} can execute query ${operationName} with variables ${variables}`, - error, - ); - // By default all queries are permitted - return true; - } -}; - -const addressCanExecuteQuery = async (request: Request): Promise => { - try { - const { operations } = detectOperation(request.body); - - if (!operations.length) { - return true; - } - const canExecuteAllOperations = await Promise.all( - operations.map( - async (operationName) => - await hasQueryPermissions(operationName, request), - ), - ); - return canExecuteAllOperations.every((canExecute) => canExecute); - } catch (error) { - /* - * If anything fails still allow the query to execute as by default all queries are permitted - */ - return true; - } -}; - -export default addressCanExecuteQuery; diff --git a/src/routes/index.ts b/src/routes/index.ts index f981368..48c04cb 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -1,5 +1,3 @@ -import { createProxyMiddleware } from "http-proxy-middleware"; - import { RouteHandler, ServerMethods, Urls } from '~types'; import { handleHealthRoute } from './health'; @@ -9,9 +7,7 @@ import { handleDeauthRoute, handleCheck, } from './auth'; -import { graphQlProxyRouteHandler, operationExecutionHandler } from './graphql'; - -export { operationExecutionHandler }; +import { handleGraphQL } from './graphql'; const routes: RouteHandler[] = [ /* @@ -49,9 +45,9 @@ const routes: RouteHandler[] = [ * GraphQL */ { - method: ServerMethods.Use, + method: ServerMethods.Post, url: Urls.GraphQL, - handler: createProxyMiddleware(graphQlProxyRouteHandler), + handler: handleGraphQL, }, ]; diff --git a/src/schema.ts b/src/schema.ts new file mode 100644 index 0000000..7c205d3 --- /dev/null +++ b/src/schema.ts @@ -0,0 +1,33 @@ +import { GraphQLSchema, print } from 'graphql'; +import { wrapSchema, schemaFromExecutor } from '@graphql-tools/wrap'; +import { AsyncExecutor } from '@graphql-tools/utils'; +import { applyMiddleware } from 'graphql-middleware'; + +import { graphqlRequest } from './helpers'; +import { permissions } from './permissions'; + +let schema: GraphQLSchema; + +const createAppSyncExecutor = (): AsyncExecutor => { + return async ({ document, variables, context }) => { + const query = print(document); + const userAddress = (context as { userAddress?: string } | undefined) + ?.userAddress; + const result = await graphqlRequest(query, variables, userAddress); + return result; + }; +}; + +export const initSchema = async (): Promise => { + const executor = createAppSyncExecutor(); + const remoteSchema = await schemaFromExecutor(executor); + + const wrappedSchema = wrapSchema({ + schema: remoteSchema, + executor, + }); + + schema = applyMiddleware(wrappedSchema, permissions); +}; + +export const getSchema = (): GraphQLSchema => schema; diff --git a/src/server.ts b/src/server.ts index a497d4d..4aa2e33 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,13 +1,12 @@ -import dotenv from "dotenv"; -import express from "express"; -import cors from "cors"; +import dotenv from 'dotenv'; +import express from 'express'; +import cors from 'cors'; import routes from '~routes'; import { getStaticOrigin, isDevMode } from './helpers'; import ExpressSession from './ExpressSession'; -import { operationExecutionHandler } from '~routes'; -import { Headers } from "~types"; +import { Headers } from '~types'; dotenv.config(); @@ -23,41 +22,41 @@ const proxyServerInstace = () => { // If there weren't any headers, or we're in devmode, just return return next(); } - let xForwardedHeadersAsString = ""; + let xForwardedHeadersAsString = ''; // So there were headers, and we're not in devmode. - if (typeof xForwardedHeaders === "string") { + if (typeof xForwardedHeaders === 'string') { xForwardedHeadersAsString = xForwardedHeaders; } else { xForwardedHeadersAsString = xForwardedHeaders.join(', '); } - if (xForwardedHeadersAsString.split(', ').at(-1) === 'https'){ + if (xForwardedHeadersAsString.split(', ').at(-1) === 'https') { req.headers[Headers.ForwardedProto] = 'https'; } return next(); }); - proxyServer.use(express.json({limit: '1mb'})); + proxyServer.use(express.json({ limit: '1mb' })); - proxyServer.use(cors({ - origin: getStaticOrigin, - credentials: true, - })); + proxyServer.use( + cors({ + origin: getStaticOrigin, + credentials: true, + }), + ); proxyServer.set('trust proxy', true); - proxyServer.use(ExpressSession({ - name: process.env.COOKIE_NAME, - secret: process.env.COOKIE_SECRET || 'pleasechangemebeforegoingintoproduction', - resave: false, - saveUninitialized: true, - cookie: { secure: !isDevMode(), sameSite: true }, - })); - - /* - * @NOTE Handle async GraphQL logic to decide if we allow a operation or not - */ - proxyServer.use(operationExecutionHandler); + proxyServer.use( + ExpressSession({ + name: process.env.COOKIE_NAME, + secret: + process.env.COOKIE_SECRET || 'pleasechangemebeforegoingintoproduction', + resave: false, + saveUninitialized: true, + cookie: { secure: !isDevMode(), sameSite: true }, + }), + ); /* * Initialize routes diff --git a/src/types.ts b/src/types.ts index 998de5c..fb05a63 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,77 +1,4 @@ -import { RequestHandler } from 'http-proxy-middleware'; - -export enum OperationTypes { - Query = 'query', - Mutation = 'mutation', -} - -export enum DefinitionTypes { - Operation = 'OperationDefinition', - Fragment = 'FragmentDefinition', -} - -export enum MutationOperations { - /* - * User - */ - CreateUniqueUser = 'createUniqueUser', - UpdateUserProfile = 'updateProfile', - UpdateUserNotificationsData = 'updateNotificationsData', - CreateTransaction = 'createTransaction', - UpdateTransaction = 'updateTransaction', - CreateUserTokens = 'createUserTokens', - CreateUserNotificationsData = 'createUserNotificationsData', - InitializeUser = 'initializeUser', - /* - * Colony - */ - CreateColonyMetadata = 'createColonyMetadata', - UpdateColonyMetadata = 'updateColonyMetadata', - ValidateUserInvite = 'validateUserInvite', - CreateColonyContributor = 'createColonyContributor', - UpdateColonyContributor = 'updateColonyContributor', - UpdateContributorsWithReputation = 'updateContributorsWithReputation', - CreateColonyEtherealMetadata = 'createColonyEtherealMetadata', - /* - * Domains - */ - CreateDomain = 'createDomain', - CreateDomainMetadata = 'createDomainMetadata', - UpdateDomainMetadata = 'updateDomainMetadata', - /* - * Actions, Mutations - */ - CreateColonyActionMetadata = 'createColonyActionMetadata', - UpdateColonyAction = 'updateColonyAction', - CreateAnnotation = 'createAnnotation', - CreateColonyDecision = 'createColonyDecision', - /* - * Tokens - */ - GetTokenFromEverywhere = 'getTokenFromEverywhere', - CreateColonyTokens = 'createColonyTokens', - DeleteColonyTokens = 'deleteColonyTokens', - /* - * Expenditures - */ - CreateExpenditureMetadata = 'createExpenditureMetadata', - CreateStreamingPaymentMetadata = 'createStreamingPaymentMetadata', - UpdateStreamingPaymentMetadata = 'updateStreamingPaymentMetadata', - /* - * Bridge / Crypto-to-fiat - */ - BridgeXYZMutation = 'bridgeXYZMutation', - BridgeCreateBankAccount = 'bridgeCreateBankAccount', - BridgeUpdateBankAccount = 'bridgeUpdateBankAccount', -} - -// All queries are allowed by default, add exceptions with specific rules here -export enum QueryOperations { - /* - * Notifications - */ - GetUserNotificationsHMAC = 'getUserNotificationsHMAC', -} +import { RequestHandler } from 'express'; export enum HttpStatuses { OK = 200, @@ -89,7 +16,7 @@ export enum ResponseTypes { Status = 'status', } -export type Response = { +export type ApiResponse = { message: string; type: ResponseTypes; data?: string | number | boolean | string[] | number[] | boolean[]; @@ -126,6 +53,7 @@ export type StaticOrigin = | string | RegExp | (boolean | string | RegExp)[]; + export type StaticOriginCallback = ( err: Error | null, origin?: StaticOrigin | undefined, @@ -142,7 +70,6 @@ export interface RouteHandler { url: Urls; handler: RequestHandler; } - export type UserRole = { id: string; role_0: boolean | null; diff --git a/tsconfig.json b/tsconfig.json index 8c7d1b4..6f3a371 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -39,9 +39,6 @@ "~routes": [ "src/routes" ], - "~queries": [ - "src/queries" - ], }, // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */