diff --git a/.gitignore b/.gitignore index 4d29575de80483b005c29bfcac5061cd2f45313e..2fa56e4996c1e603ab14990da32b5c2353cfe189 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ /coverage # production -/build +/dist # misc .DS_Store diff --git a/grpc.txt b/grpc.txt deleted file mode 100644 index eb0d24e4ff75a1d4c15694e7efa040a3ba8433b7..0000000000000000000000000000000000000000 --- a/grpc.txt +++ /dev/null @@ -1,21 +0,0 @@ ------GRPC TESTS----- -randomProduct -39.50119981765747 -product -37.52583360671997 -allProducts -31.22728362083435 -categories -29.51584138870239 -allOrders -52.35934963226318 -ordersByUser -42.86946668624878 -user -31.468091630935668 -allUsers -31.84824995994568 -postOrder -32.73759984970093 -patchUser -34.883433198928834 \ No newline at end of file diff --git a/grpcTests.js b/grpcTests.js deleted file mode 100644 index 97c907400290883e5fdc27c4aacd3b0f7e6b7a7f..0000000000000000000000000000000000000000 --- a/grpcTests.js +++ /dev/null @@ -1,183 +0,0 @@ -const grpc = require("@grpc/grpc-js"); -const protoLoader = require("@grpc/proto-loader"); - -const PROTO_PATH = "./app.proto"; - -const packageDefinition = protoLoader.loadSync(PROTO_PATH, { - keepCase: true, - longs: String, - enums: String, - defaults: true, - oneofs: true, -}); - -const app = grpc.loadPackageDefinition(packageDefinition); - -const getTests = (endpoint, user, product) => { - const client = new app.App(endpoint, grpc.credentials.createInsecure()); - - return [ - { - name: "randomProduct", - run: async () => { - const response = await new Promise((resolve, reject) => { - client.getRandomProduct({}, function (_err, response) { - if (_err) reject(_err); - resolve(response); - }); - }); - - return [0, response]; - }, - }, - { - name: "product", - run: async () => { - const response = await new Promise((resolve, reject) => { - client.getProduct( - { product_id: product }, - function (_err, response) { - if (_err) reject(_err); - resolve(response); - }, - ); - }); - - return [0, response]; - }, - }, - { - name: "allProducts", - run: async () => { - const response = await new Promise((resolve, reject) => { - client.getAllProducts({}, function (_err, response) { - if (_err) reject(_err); - resolve(response); - }); - }); - - return [0, response]; - }, - }, - { - name: "categories", - run: async () => { - const response = await new Promise((resolve, reject) => { - client.getAllCategories({}, function (_err, response) { - if (_err) reject(_err); - resolve(response); - }); - }); - - return [0, response]; - }, - }, - { - name: "allOrders", - run: async () => { - const response = await new Promise((resolve, reject) => { - client.getAllOrders({}, function (_err, response) { - if (_err) reject(_err); - resolve(response); - }); - }); - - return [0, response]; - }, - }, - { - name: "ordersByUser", - run: async () => { - const response = await new Promise((resolve, reject) => { - client.getAllUserOrders( - { id: user }, - function (_err, response) { - if (_err) reject(_err); - resolve(response); - }, - ); - }); - - return [0, response]; - }, - }, - { - name: "user", - run: async () => { - const response = await new Promise((resolve, reject) => { - client.getUser( - { id: user }, - function (_err, response) { - if (_err) reject(_err); - resolve(response); - }, - ); - }); - - return [0, response]; - }, - }, - { - name: "allUsers", - run: async () => { - const response = await new Promise((resolve, reject) => { - client.getAllUsers({}, function (_err, response) { - if (_err) reject(_err); - resolve(response); - }); - }); - - return [0, response]; - }, - }, - { - name: "postOrder", - run: async () => { - { - const response = await new Promise((resolve, reject) => { - client.postOrder( - { - user_id: user, - products: [ - { - product_id: product, - quantity: 2, - }, - ], - total_amount: 600.0, - }, - function (_err, response) { - if (_err) reject(_err); - resolve(response); - }, - ); - }); - - return [0, response]; - } - }, - }, - { - name: "patchUser", - run: async () => { - const response = await new Promise((resolve, reject) => { - client.patchAccountDetails( - { - id: user, - email: "update@test.com", - name: "update test", - }, - function (_err, response) { - if (_err) reject(_err); - resolve(response); - }, - ); - }); - - return [0, response]; - }, - }, - ]; -}; - -module.exports = { getTests }; diff --git a/main.js b/main.js deleted file mode 100644 index 0705a5651cece1cda982c73c7c14e15994d2e3ec..0000000000000000000000000000000000000000 --- a/main.js +++ /dev/null @@ -1,55 +0,0 @@ -const rest = require("./restTests"); -const grpc = require("./grpcTests"); -const { performance } = require("perf_hooks"); - -const REST_ENDPOINT = "http://18.209.23.10:3000"; -const GRPC_ENDPOINT = "18.209.23.10:3001"; - -const runTest = async (func) => { - const start = performance.now(); - await func(); - const end = performance.now(); - return end-start; -} - -const avgRuntime = async (func, times) => { - const promises = []; - for (let i = 0; i < times; i++) { - promises.push(runTest(func)); - } - - const results = await Promise.all(promises); - return results.reduce((acc, curr) => acc + curr) / times; -}; - -async function getExamples() { - let resp = await fetch(REST_ENDPOINT + "/randomproduct"); - let body = await resp.json(); - const prodId = body.id; - - resp = await fetch(REST_ENDPOINT + "/users"); - body = await resp.json(); - const userId = body[0].id; - - return { product: prodId, user: userId }; -} - -async function runTests() { - const {user, product} = await getExamples(); - - const ITERATIONS = 5; - - console.log("-----GRPC TESTS-----"); - for (const test of grpc.getTests(GRPC_ENDPOINT, user, product)) { - console.log(test.name); - console.log(await avgRuntime(test.run, ITERATIONS)); - } - - console.log("-----REST TESTS-----"); - for (const test of rest.getTests(REST_ENDPOINT, user, product)) { - console.log(test.name); - console.log(await avgRuntime(test.run, ITERATIONS)); - } -} - -runTests(); diff --git a/package-lock.json b/package-lock.json index 855c6688ae4d581fe7486b7d8add1e4b513d4898..8925fd523b1c1e083529793da6a5cc87580f7ee0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,22 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@grpc/grpc-js": "^1.9.0", + "@grpc/grpc-js": "1.9.0", "@grpc/proto-loader": "^0.7.8", + "@types/express": "^4.17.21", "benchmark": "^2.1.4", - "google-protobuf": "^3.21.2" + "express": "^4.18.2", + "express-ws": "^5.0.2", + "google-protobuf": "^3.21.2", + "nice-grpc": "^2.1.7", + "path": "^0.12.7", + "typescript": "^5.3.3", + "uuidv4": "^6.2.13" + }, + "devDependencies": { + "@types/express-ws": "^3.0.4", + "grpc-tools": "^1.12.4", + "ts-proto": "^1.167.2" } }, "node_modules/@grpc/grpc-js": { @@ -28,13 +40,12 @@ } }, "node_modules/@grpc/proto-loader": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.8.tgz", - "integrity": "sha512-GU12e2c8dmdXb7XUlOgYWZ2o2i+z9/VeACkxTA/zzAe2IjclC5PnVL0lpgjhrqfpDYHzM8B1TF6pqWegMYAzlA==", + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.9.tgz", + "integrity": "sha512-YJsOehVXzgurc+lLAxYnlSMc1p/Gu6VAvnfx0ATi2nzvr0YZcjhmZDeY8SeAKv1M7zE3aEJH0Xo9mK1iZ8GYoQ==", "dependencies": { - "@types/long": "^4.0.1", "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", + "long": "^5.0.0", "protobufjs": "^7.2.4", "yargs": "^17.7.2" }, @@ -45,6 +56,26 @@ "node": ">=6" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -99,15 +130,148 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.43", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", + "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express-ws": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/express-ws/-/express-ws-3.0.4.tgz", + "integrity": "sha512-Yjj18CaivG5KndgcvzttWe8mPFinPCHJC2wvyQqVzA7hqeufM8EtWMj6mpp5omg3s8XALUexhOu8aXAyi/DyJQ==", + "dev": true, + "dependencies": { + "@types/express": "*", + "@types/express-serve-static-core": "*", + "@types/ws": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, + "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==" }, "node_modules/@types/node": { - "version": "20.4.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.7.tgz", - "integrity": "sha512-bUBrPjEry2QUTsnuEjzjbS7voGWCc30W0qzgMf90GPeDGFRakvrz47ju+oqDAKCXLUCe39u57/ORMl/O/04/9g==" + "version": "20.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.0.tgz", + "integrity": "sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==" + }, + "node_modules/@types/qs": { + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==" + }, + "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==" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "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==", + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==" + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/abort-controller-x": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/abort-controller-x/-/abort-controller-x-0.4.3.tgz", + "integrity": "sha512-VtUwTNU8fpMwvWGn4xE93ywbogTYsuT+AUxAXOeelbXuQVIwNmC5YLeho9sH4vZ4ITW8414TTAOG1nW6uIVHCA==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } }, "node_modules/ansi-regex": { "version": "5.0.1", @@ -131,6 +295,36 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, "node_modules/benchmark": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", @@ -140,6 +334,62 @@ "platform": "^1.3.3" } }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz", + "integrity": "sha512-Mj50FLHtlsoVfRfnHaZvyrooHcrlceNZdL/QBvJJVd9Ta55qCQK0gs4ss2oZDeV9zFCs6ewzYgVE5yfVmfFpVg==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "set-function-length": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/case-anything": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", + "integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==", + "dev": true, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -169,11 +419,169 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/define-data-property": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.2.tgz", + "integrity": "sha512-SRtsSqsDbgpJBbW3pABMCOt6rQyeM8s8RiyeSN8jYG8sYmt/kGJejbydttUsnDs1tadr19tvhT4ShwMyoqAm4g==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.2", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dprint-node": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/dprint-node/-/dprint-node-1.0.8.tgz", + "integrity": "sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==", + "dev": true, + "dependencies": { + "detect-libc": "^1.0.3" + } + }, + "node_modules/dprint-node/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -182,131 +590,1270 @@ "node": ">=6" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">= 0.6" } }, - "node_modules/google-protobuf": { - "version": "3.21.2", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz", - "integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==" + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/express-ws": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/express-ws/-/express-ws-5.0.2.tgz", + "integrity": "sha512-0uvmuk61O9HXgLhGl3QhNSEtRsQevtmbL94/eILaliEADZBHZOQUAiHFrGPrgsjikohyrmSG5g+sCfASTt0lkQ==", + "dependencies": { + "ws": "^7.4.6" + }, "engines": { - "node": ">=8" + "node": ">=4.5.0" + }, + "peerDependencies": { + "express": "^4.0.0 || ^5.0.0-alpha.1" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "node_modules/express/node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/platform": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" + "node_modules/express/node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } }, - "node_modules/protobufjs": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.4.tgz", - "integrity": "sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ==", - "hasInstallScript": true, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">= 0.8" } }, - "node_modules/protobufjs/node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "minipass": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "yallist": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "engines": { - "node": ">=10" + "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/google-protobuf": { + "version": "3.21.2", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz", + "integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==" + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/grpc-tools": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/grpc-tools/-/grpc-tools-1.12.4.tgz", + "integrity": "sha512-5+mLAJJma3BjnW/KQp6JBjUMgvu7Mu3dBvBPd1dcbNIb+qiR0817zDpgPjS7gRb+l/8EVNIa3cB02xI9JLToKg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.5" + }, + "bin": { + "grpc_tools_node_protoc": "bin/protoc.js", + "grpc_tools_node_protoc_plugin": "bin/protoc_plugin.js" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nice-grpc": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/nice-grpc/-/nice-grpc-2.1.7.tgz", + "integrity": "sha512-pSaZk5Y3PHGAPObOSXTrANgimA6T//szxlcKOnnyttpYwO0gyOpX2WsaFK4fbGJizPVxXjwqrXpPOSHMwM2vlg==", + "dependencies": { + "@grpc/grpc-js": "^1.9.5", + "abort-controller-x": "^0.4.0", + "nice-grpc-common": "^2.0.2" + } + }, + "node_modules/nice-grpc-common": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/nice-grpc-common/-/nice-grpc-common-2.0.2.tgz", + "integrity": "sha512-7RNWbls5kAL1QVUOXvBsv1uO0wPQK3lHv+cY1gwkTzirnG1Nop4cBJZubpgziNbaVc/bl9QJcyvsf/NQxa3rjQ==", + "dependencies": { + "ts-error": "^1.0.6" + } + }, + "node_modules/nice-grpc/node_modules/@grpc/grpc-js": { + "version": "1.9.14", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.14.tgz", + "integrity": "sha512-nOpuzZ2G3IuMFN+UPPpKrC6NsLmWsTqSsm66IRfnBt1D4pwTqE27lmbpcPM+l2Ua4gE7PfjRHI6uedAy7hoXUw==", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/protobufjs": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/set-function-length": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "dependencies": { + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", + "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/ts-error": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/ts-error/-/ts-error-1.0.6.tgz", + "integrity": "sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==" + }, + "node_modules/ts-poet": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/ts-poet/-/ts-poet-6.6.0.tgz", + "integrity": "sha512-4vEH/wkhcjRPFOdBwIh9ItO6jOoumVLRF4aABDX5JSNEubSqwOulihxQPqai+OkuygJm3WYMInxXQX4QwVNMuw==", + "dev": true, + "dependencies": { + "dprint-node": "^1.0.7" + } + }, + "node_modules/ts-proto": { + "version": "1.167.2", + "resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-1.167.2.tgz", + "integrity": "sha512-7y/BLjiUZphgCe+SZBEG20DP94VK7BHpHcl5fkeN8lRCeABNIsiI54FkUQ8pe7PsHLVpFKqMO5aRLx74FX+4iA==", + "dev": true, + "dependencies": { + "case-anything": "^2.1.13", + "protobufjs": "^7.2.4", + "ts-poet": "^6.5.0", + "ts-proto-descriptors": "1.15.0" + }, + "bin": { + "protoc-gen-ts_proto": "protoc-gen-ts_proto" + } + }, + "node_modules/ts-proto-descriptors": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-1.15.0.tgz", + "integrity": "sha512-TYyJ7+H+7Jsqawdv+mfsEpZPTIj9siDHS6EMCzG/z3b/PZiphsX+mWtqFfFVe5/N0Th6V3elK9lQqjnrgTOfrg==", + "dev": true, + "dependencies": { + "long": "^5.2.3", + "protobufjs": "^7.2.4" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uuidv4": { + "version": "6.2.13", + "resolved": "https://registry.npmjs.org/uuidv4/-/uuidv4-6.2.13.tgz", + "integrity": "sha512-AXyzMjazYB3ovL3q051VLH06Ixj//Knx7QnUSi1T//Ie3io6CpsPu9nVMOx5MoLWh6xV0B9J0hIaxungxXUbPQ==", + "dependencies": { + "@types/uuid": "8.3.4", + "uuid": "8.3.2" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/package.json b/package.json index 80ce57316af5813942b949269e4f2098dab165f9..25c8beeb07b6d4b0856324908fcaf29c3b2e847b 100644 --- a/package.json +++ b/package.json @@ -2,17 +2,29 @@ "name": "lab1-measure", "version": "1.0.0", "description": "", - "main": "main.js", + "main": "dist/main.js", "scripts": { - "start": "node main.js", + "start": "tsc && cp public/* dist && node dist/main.js", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "based JAML", "license": "ISC", "dependencies": { - "@grpc/grpc-js": "^1.9.0", + "@grpc/grpc-js": "1.9.0", "@grpc/proto-loader": "^0.7.8", + "@types/express": "^4.17.21", "benchmark": "^2.1.4", - "google-protobuf": "^3.21.2" + "express": "^4.18.2", + "express-ws": "^5.0.2", + "google-protobuf": "^3.21.2", + "nice-grpc": "^2.1.7", + "path": "^0.12.7", + "typescript": "^5.3.3", + "uuidv4": "^6.2.13" + }, + "devDependencies": { + "@types/express-ws": "^3.0.4", + "grpc-tools": "^1.12.4", + "ts-proto": "^1.167.2" } -} +} \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000000000000000000000000000000000000..cdc6e56604d2eaacba279ae513722eb08dbcf9f6 --- /dev/null +++ b/public/index.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>SE464 Lab Testing</title> +</head> + +<body> + <h1>SE 464 Benchmarker</h1> + <noscript> + For full functionality of this site it is necessary to enable JavaScript. + Here are the <a href="https://www.enable-javascript.com/"> + instructions how to enable JavaScript in your web browser</a>. + </noscript> + <p>Input your AWS EC2/LoadBalancer URL</p> + <form action="viewResults" method="post"> + <input type="text" name="url" id="url" required> + <input type="submit" value="Submit"> +</body> + +</html> \ No newline at end of file diff --git a/public/results.html b/public/results.html new file mode 100644 index 0000000000000000000000000000000000000000..d7b4c560c5964c24905f3616bae682ad0e5632b5 --- /dev/null +++ b/public/results.html @@ -0,0 +1,101 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>SE464 Lab Test Results</title> + <style> + table { + border-collapse: collapse; + } + + th, + td { + border: 1px solid #dddddd; + text-align: left; + padding: 8px; + } + + .success { + color: green; + } + + .fail { + color: orange; + } + + tr:nth-child(even) { + background-color: #f2f2f2; + } + </style> +</head> + +<body> + <h1>Results for SE464 <span id="aws-url"></span></h1> + <div> + <label for="iterations">Rerun with number of iterations:</label> + <input type="text" id="iterations"> + <button type="button">Submit</button> + </div> + <div id="results"></div> +</body> + +<script> + const urlParts = window.location.href.split('/'); + const id = urlParts[urlParts.length - 1] + const awsUrl = atob(id); + const titleHeading = document.getElementById('aws-url'); + titleHeading.innerHTML = `${awsUrl}`; + + const socket = new WebSocket(`/results/${id}`); + + // Listen for messages + socket.addEventListener("message", (event) => { + try { + const data = JSON.parse(event.data); + const restJsonData = data["rest"]; + const grpcJsonData = data["grpc"]; + document.getElementById('results').replaceChildren(jsonToTable("REST", restJsonData)); + document.getElementById('results').appendChild(jsonToTable("gRPC", grpcJsonData)); + } catch (e) { + console.error(e); + } + }); + + document.querySelector('button').addEventListener('click', () => { + const iterations = document.getElementById('iterations').value; + socket.send(iterations); + }); + + function jsonToTable(title, jsonData) { + // Create a table + const div = document.createElement('div'); + const titleElement = document.createElement('h2'); + titleElement.textContent = title; + div.appendChild(titleElement); + + const table = document.createElement('table'); + + for (const [k, v] of Object.entries(jsonData)) { + const row = document.createElement('tr'); + const td1 = document.createElement('td'); + const td2 = document.createElement('td'); + td1.textContent = k; + td2.textContent = v["time"] + ' ms'; + td2.className = v["ok"] ? 'success' : 'fail'; + row.appendChild(td1); + row.appendChild(td2); + + table.appendChild(row); + } + + div.appendChild(table); + + // Return the table + return div; + } + +</script> + +</html> \ No newline at end of file diff --git a/rest.txt b/rest.txt deleted file mode 100644 index 05c8fe891c839b2f452431ed9336801e5b59adfc..0000000000000000000000000000000000000000 --- a/rest.txt +++ /dev/null @@ -1,21 +0,0 @@ ------REST TESTS----- -randomProduct -50.12581658363342 -product -33.34259166717529 -allProducts -34.9744167804718 -categories -32.45142498016357 -allOrders -62.56571636199951 -ordersByUser -52.71434187889099 -user -31.619408416748048 -allUsers -31.58545837402344 -postOrder -36.04820818901062 -patchUser -35.95475001335144 \ No newline at end of file diff --git a/restTests.js b/restTests.js deleted file mode 100644 index 00dd82324658522847e929204e1df9308420e3aa..0000000000000000000000000000000000000000 --- a/restTests.js +++ /dev/null @@ -1,129 +0,0 @@ -const getTests = (endpoint, user, product) => [ - // { - // run: async () => { - // const resp = await fetch(endpoint + "/"); - // const body = await resp.json(); - // return [resp.status, body]; - // }, - // }, - { - name: "randomProduct", - run: async () => { - const resp = await fetch(endpoint + "/randomproduct"); - const body = await resp.json(); - return [resp.status, body]; - }, - }, - { - name: "product", - run: async () => { - const resp = await fetch( - `${endpoint}/product/${product}`, - ); - const body = await resp.json(); - return [resp.status, body]; - }, - }, - { - name: "allProducts", - run: async () => { - const resp = await fetch(endpoint + "/products"); - const body = await resp.json(); - return [resp.status, body]; - }, - }, - { - name: "categories", - run: async () => { - const resp = await fetch(endpoint + "/categories"); - const body = await resp.json(); - return [resp.status, body]; - }, - }, - { - name: "allOrders", - run: async () => { - const resp = await fetch(endpoint + "/allorders"); - const body = await resp.json(); - return [resp.status, body]; - }, - }, - { - name: "ordersByUser", - run: async () => { - const resp = await fetch(`${endpoint}/orders?userId=${user}`); - const body = await resp.json(); - return [resp.status, body]; - }, - }, - { - name: "user", - run: async () => { - const resp = await fetch( - `${endpoint}/user/${user}`, - ); - const body = await resp.json(); - return [resp.status, body]; - }, - }, - { - name: "allUsers", - run: async () => { - const resp = await fetch(endpoint + "/users"); - const body = await resp.json(); - return [resp.status, body]; - }, - }, - { - name: "postOrder", - run: async () => { - const resp = await fetch(endpoint + "/orders", { - method: "POST", - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - user_id: user, - products: [ - { - product_id: product, - quantity: 2, - }, - ], - total_amount: 600.0, - }), - }); - - const body = null; - - return [resp.status, body]; - }, - }, - { - name: "patchUser", - run: async () => { - const resp = await fetch( - endpoint + `/user/${user}`, - { - method: "PATCH", - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - id: user, - email: "update@test.com", - name: "update test", - }), - }, - ); - - const body = null; - - return [resp.status, body]; - }, - }, -]; - -module.exports = { getTests }; diff --git a/src/compiled_proto/app.ts b/src/compiled_proto/app.ts new file mode 100644 index 0000000000000000000000000000000000000000..3662a5cac1f73daa3195ba33d2eedc46ca69c8c4 --- /dev/null +++ b/src/compiled_proto/app.ts @@ -0,0 +1,1316 @@ +/* eslint-disable */ +import type { CallContext, CallOptions } from "nice-grpc-common"; +import * as _m0 from "protobufjs/minimal"; +import Long = require("long"); + +export const protobufPackage = ""; + +export interface EmptyRequest { +} + +export interface AllProductsRequest { + categoryId?: string | undefined; +} + +export interface ProductRequest { + productId: string; +} + +export interface Product { + categoryId: string; + stock: number; + price: number; + description: string; + id: string; + name: string; +} + +export interface Products { + products: Product[]; +} + +export interface OrderProduct { + productId: string; + quantity: number; +} + +export interface Order { + userId: string; + id: string; + products: OrderProduct[]; + totalAmount: number; +} + +export interface OrderRequest { + id: string; +} + +export interface Orders { + orders: Order[]; +} + +export interface Category { + description: string; + id: string; + price: number; + name: string; +} + +export interface Categories { + categories: Category[]; +} + +/** Users */ +export interface UserRequest { + id: string; +} + +export interface UserPatchRequest { + /** We assign user a unique ID, this shouldn't be changed. Following fields can be changed */ + id: string; + email: string; + password: string; +} + +export interface User { + id: string; + email: string; +} + +export interface Users { + users: User[]; +} + +function createBaseEmptyRequest(): EmptyRequest { + return {}; +} + +export const EmptyRequest = { + encode(_: EmptyRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): EmptyRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEmptyRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): EmptyRequest { + return {}; + }, + + toJSON(_: EmptyRequest): unknown { + const obj: any = {}; + return obj; + }, + + create(base?: DeepPartial<EmptyRequest>): EmptyRequest { + return EmptyRequest.fromPartial(base ?? {}); + }, + fromPartial(_: DeepPartial<EmptyRequest>): EmptyRequest { + const message = createBaseEmptyRequest(); + return message; + }, +}; + +function createBaseAllProductsRequest(): AllProductsRequest { + return { categoryId: undefined }; +} + +export const AllProductsRequest = { + encode(message: AllProductsRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.categoryId !== undefined) { + writer.uint32(10).string(message.categoryId); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AllProductsRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAllProductsRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.categoryId = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AllProductsRequest { + return { categoryId: isSet(object.categoryId) ? globalThis.String(object.categoryId) : undefined }; + }, + + toJSON(message: AllProductsRequest): unknown { + const obj: any = {}; + if (message.categoryId !== undefined) { + obj.categoryId = message.categoryId; + } + return obj; + }, + + create(base?: DeepPartial<AllProductsRequest>): AllProductsRequest { + return AllProductsRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial<AllProductsRequest>): AllProductsRequest { + const message = createBaseAllProductsRequest(); + message.categoryId = object.categoryId ?? undefined; + return message; + }, +}; + +function createBaseProductRequest(): ProductRequest { + return { productId: "" }; +} + +export const ProductRequest = { + encode(message: ProductRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.productId !== "") { + writer.uint32(10).string(message.productId); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ProductRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseProductRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.productId = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ProductRequest { + return { productId: isSet(object.productId) ? globalThis.String(object.productId) : "" }; + }, + + toJSON(message: ProductRequest): unknown { + const obj: any = {}; + if (message.productId !== "") { + obj.productId = message.productId; + } + return obj; + }, + + create(base?: DeepPartial<ProductRequest>): ProductRequest { + return ProductRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial<ProductRequest>): ProductRequest { + const message = createBaseProductRequest(); + message.productId = object.productId ?? ""; + return message; + }, +}; + +function createBaseProduct(): Product { + return { categoryId: "", stock: 0, price: 0, description: "", id: "", name: "" }; +} + +export const Product = { + encode(message: Product, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.categoryId !== "") { + writer.uint32(10).string(message.categoryId); + } + if (message.stock !== 0) { + writer.uint32(24).int64(message.stock); + } + if (message.price !== 0) { + writer.uint32(32).int64(message.price); + } + if (message.description !== "") { + writer.uint32(42).string(message.description); + } + if (message.id !== "") { + writer.uint32(50).string(message.id); + } + if (message.name !== "") { + writer.uint32(58).string(message.name); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Product { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseProduct(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.categoryId = reader.string(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.stock = longToNumber(reader.int64() as Long); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.price = longToNumber(reader.int64() as Long); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.description = reader.string(); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.id = reader.string(); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.name = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Product { + return { + categoryId: isSet(object.categoryId) ? globalThis.String(object.categoryId) : "", + stock: isSet(object.stock) ? globalThis.Number(object.stock) : 0, + price: isSet(object.price) ? globalThis.Number(object.price) : 0, + description: isSet(object.description) ? globalThis.String(object.description) : "", + id: isSet(object.id) ? globalThis.String(object.id) : "", + name: isSet(object.name) ? globalThis.String(object.name) : "", + }; + }, + + toJSON(message: Product): unknown { + const obj: any = {}; + if (message.categoryId !== "") { + obj.categoryId = message.categoryId; + } + if (message.stock !== 0) { + obj.stock = Math.round(message.stock); + } + if (message.price !== 0) { + obj.price = Math.round(message.price); + } + if (message.description !== "") { + obj.description = message.description; + } + if (message.id !== "") { + obj.id = message.id; + } + if (message.name !== "") { + obj.name = message.name; + } + return obj; + }, + + create(base?: DeepPartial<Product>): Product { + return Product.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial<Product>): Product { + const message = createBaseProduct(); + message.categoryId = object.categoryId ?? ""; + message.stock = object.stock ?? 0; + message.price = object.price ?? 0; + message.description = object.description ?? ""; + message.id = object.id ?? ""; + message.name = object.name ?? ""; + return message; + }, +}; + +function createBaseProducts(): Products { + return { products: [] }; +} + +export const Products = { + encode(message: Products, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.products) { + Product.encode(v!, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Products { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseProducts(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.products.push(Product.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Products { + return { + products: globalThis.Array.isArray(object?.products) ? object.products.map((e: any) => Product.fromJSON(e)) : [], + }; + }, + + toJSON(message: Products): unknown { + const obj: any = {}; + if (message.products?.length) { + obj.products = message.products.map((e) => Product.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial<Products>): Products { + return Products.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial<Products>): Products { + const message = createBaseProducts(); + message.products = object.products?.map((e) => Product.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseOrderProduct(): OrderProduct { + return { productId: "", quantity: 0 }; +} + +export const OrderProduct = { + encode(message: OrderProduct, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.productId !== "") { + writer.uint32(10).string(message.productId); + } + if (message.quantity !== 0) { + writer.uint32(16).int64(message.quantity); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): OrderProduct { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseOrderProduct(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.productId = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.quantity = longToNumber(reader.int64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): OrderProduct { + return { + productId: isSet(object.productId) ? globalThis.String(object.productId) : "", + quantity: isSet(object.quantity) ? globalThis.Number(object.quantity) : 0, + }; + }, + + toJSON(message: OrderProduct): unknown { + const obj: any = {}; + if (message.productId !== "") { + obj.productId = message.productId; + } + if (message.quantity !== 0) { + obj.quantity = Math.round(message.quantity); + } + return obj; + }, + + create(base?: DeepPartial<OrderProduct>): OrderProduct { + return OrderProduct.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial<OrderProduct>): OrderProduct { + const message = createBaseOrderProduct(); + message.productId = object.productId ?? ""; + message.quantity = object.quantity ?? 0; + return message; + }, +}; + +function createBaseOrder(): Order { + return { userId: "", id: "", products: [], totalAmount: 0 }; +} + +export const Order = { + encode(message: Order, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.userId !== "") { + writer.uint32(10).string(message.userId); + } + if (message.id !== "") { + writer.uint32(18).string(message.id); + } + for (const v of message.products) { + OrderProduct.encode(v!, writer.uint32(26).fork()).ldelim(); + } + if (message.totalAmount !== 0) { + writer.uint32(32).int64(message.totalAmount); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Order { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseOrder(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.userId = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.id = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.products.push(OrderProduct.decode(reader, reader.uint32())); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.totalAmount = longToNumber(reader.int64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Order { + return { + userId: isSet(object.userId) ? globalThis.String(object.userId) : "", + id: isSet(object.id) ? globalThis.String(object.id) : "", + products: globalThis.Array.isArray(object?.products) + ? object.products.map((e: any) => OrderProduct.fromJSON(e)) + : [], + totalAmount: isSet(object.totalAmount) ? globalThis.Number(object.totalAmount) : 0, + }; + }, + + toJSON(message: Order): unknown { + const obj: any = {}; + if (message.userId !== "") { + obj.userId = message.userId; + } + if (message.id !== "") { + obj.id = message.id; + } + if (message.products?.length) { + obj.products = message.products.map((e) => OrderProduct.toJSON(e)); + } + if (message.totalAmount !== 0) { + obj.totalAmount = Math.round(message.totalAmount); + } + return obj; + }, + + create(base?: DeepPartial<Order>): Order { + return Order.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial<Order>): Order { + const message = createBaseOrder(); + message.userId = object.userId ?? ""; + message.id = object.id ?? ""; + message.products = object.products?.map((e) => OrderProduct.fromPartial(e)) || []; + message.totalAmount = object.totalAmount ?? 0; + return message; + }, +}; + +function createBaseOrderRequest(): OrderRequest { + return { id: "" }; +} + +export const OrderRequest = { + encode(message: OrderRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): OrderRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseOrderRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.id = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): OrderRequest { + return { id: isSet(object.id) ? globalThis.String(object.id) : "" }; + }, + + toJSON(message: OrderRequest): unknown { + const obj: any = {}; + if (message.id !== "") { + obj.id = message.id; + } + return obj; + }, + + create(base?: DeepPartial<OrderRequest>): OrderRequest { + return OrderRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial<OrderRequest>): OrderRequest { + const message = createBaseOrderRequest(); + message.id = object.id ?? ""; + return message; + }, +}; + +function createBaseOrders(): Orders { + return { orders: [] }; +} + +export const Orders = { + encode(message: Orders, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.orders) { + Order.encode(v!, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Orders { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseOrders(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.orders.push(Order.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Orders { + return { orders: globalThis.Array.isArray(object?.orders) ? object.orders.map((e: any) => Order.fromJSON(e)) : [] }; + }, + + toJSON(message: Orders): unknown { + const obj: any = {}; + if (message.orders?.length) { + obj.orders = message.orders.map((e) => Order.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial<Orders>): Orders { + return Orders.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial<Orders>): Orders { + const message = createBaseOrders(); + message.orders = object.orders?.map((e) => Order.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseCategory(): Category { + return { description: "", id: "", price: 0, name: "" }; +} + +export const Category = { + encode(message: Category, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.description !== "") { + writer.uint32(10).string(message.description); + } + if (message.id !== "") { + writer.uint32(18).string(message.id); + } + if (message.price !== 0) { + writer.uint32(24).int64(message.price); + } + if (message.name !== "") { + writer.uint32(34).string(message.name); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Category { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseCategory(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.description = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.id = reader.string(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.price = longToNumber(reader.int64() as Long); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.name = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Category { + return { + description: isSet(object.description) ? globalThis.String(object.description) : "", + id: isSet(object.id) ? globalThis.String(object.id) : "", + price: isSet(object.price) ? globalThis.Number(object.price) : 0, + name: isSet(object.name) ? globalThis.String(object.name) : "", + }; + }, + + toJSON(message: Category): unknown { + const obj: any = {}; + if (message.description !== "") { + obj.description = message.description; + } + if (message.id !== "") { + obj.id = message.id; + } + if (message.price !== 0) { + obj.price = Math.round(message.price); + } + if (message.name !== "") { + obj.name = message.name; + } + return obj; + }, + + create(base?: DeepPartial<Category>): Category { + return Category.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial<Category>): Category { + const message = createBaseCategory(); + message.description = object.description ?? ""; + message.id = object.id ?? ""; + message.price = object.price ?? 0; + message.name = object.name ?? ""; + return message; + }, +}; + +function createBaseCategories(): Categories { + return { categories: [] }; +} + +export const Categories = { + encode(message: Categories, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.categories) { + Category.encode(v!, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Categories { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseCategories(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.categories.push(Category.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Categories { + return { + categories: globalThis.Array.isArray(object?.categories) + ? object.categories.map((e: any) => Category.fromJSON(e)) + : [], + }; + }, + + toJSON(message: Categories): unknown { + const obj: any = {}; + if (message.categories?.length) { + obj.categories = message.categories.map((e) => Category.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial<Categories>): Categories { + return Categories.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial<Categories>): Categories { + const message = createBaseCategories(); + message.categories = object.categories?.map((e) => Category.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseUserRequest(): UserRequest { + return { id: "" }; +} + +export const UserRequest = { + encode(message: UserRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): UserRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUserRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.id = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): UserRequest { + return { id: isSet(object.id) ? globalThis.String(object.id) : "" }; + }, + + toJSON(message: UserRequest): unknown { + const obj: any = {}; + if (message.id !== "") { + obj.id = message.id; + } + return obj; + }, + + create(base?: DeepPartial<UserRequest>): UserRequest { + return UserRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial<UserRequest>): UserRequest { + const message = createBaseUserRequest(); + message.id = object.id ?? ""; + return message; + }, +}; + +function createBaseUserPatchRequest(): UserPatchRequest { + return { id: "", email: "", password: "" }; +} + +export const UserPatchRequest = { + encode(message: UserPatchRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + if (message.email !== "") { + writer.uint32(18).string(message.email); + } + if (message.password !== "") { + writer.uint32(26).string(message.password); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): UserPatchRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUserPatchRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.email = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.password = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): UserPatchRequest { + return { + id: isSet(object.id) ? globalThis.String(object.id) : "", + email: isSet(object.email) ? globalThis.String(object.email) : "", + password: isSet(object.password) ? globalThis.String(object.password) : "", + }; + }, + + toJSON(message: UserPatchRequest): unknown { + const obj: any = {}; + if (message.id !== "") { + obj.id = message.id; + } + if (message.email !== "") { + obj.email = message.email; + } + if (message.password !== "") { + obj.password = message.password; + } + return obj; + }, + + create(base?: DeepPartial<UserPatchRequest>): UserPatchRequest { + return UserPatchRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial<UserPatchRequest>): UserPatchRequest { + const message = createBaseUserPatchRequest(); + message.id = object.id ?? ""; + message.email = object.email ?? ""; + message.password = object.password ?? ""; + return message; + }, +}; + +function createBaseUser(): User { + return { id: "", email: "" }; +} + +export const User = { + encode(message: User, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + if (message.email !== "") { + writer.uint32(18).string(message.email); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): User { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUser(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.email = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): User { + return { + id: isSet(object.id) ? globalThis.String(object.id) : "", + email: isSet(object.email) ? globalThis.String(object.email) : "", + }; + }, + + toJSON(message: User): unknown { + const obj: any = {}; + if (message.id !== "") { + obj.id = message.id; + } + if (message.email !== "") { + obj.email = message.email; + } + return obj; + }, + + create(base?: DeepPartial<User>): User { + return User.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial<User>): User { + const message = createBaseUser(); + message.id = object.id ?? ""; + message.email = object.email ?? ""; + return message; + }, +}; + +function createBaseUsers(): Users { + return { users: [] }; +} + +export const Users = { + encode(message: Users, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.users) { + User.encode(v!, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Users { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUsers(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.users.push(User.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Users { + return { users: globalThis.Array.isArray(object?.users) ? object.users.map((e: any) => User.fromJSON(e)) : [] }; + }, + + toJSON(message: Users): unknown { + const obj: any = {}; + if (message.users?.length) { + obj.users = message.users.map((e) => User.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial<Users>): Users { + return Users.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial<Users>): Users { + const message = createBaseUsers(); + message.users = object.users?.map((e) => User.fromPartial(e)) || []; + return message; + }, +}; + +export type AppDefinition = typeof AppDefinition; +export const AppDefinition = { + name: "App", + fullName: "App", + methods: { + getProduct: { + name: "GetProduct", + requestType: ProductRequest, + requestStream: false, + responseType: Product, + responseStream: false, + options: {}, + }, + getRandomProduct: { + name: "GetRandomProduct", + requestType: EmptyRequest, + requestStream: false, + responseType: Product, + responseStream: false, + options: {}, + }, + getAllProducts: { + name: "GetAllProducts", + requestType: AllProductsRequest, + requestStream: false, + responseType: Products, + responseStream: false, + options: {}, + }, + getAllCategories: { + name: "GetAllCategories", + requestType: EmptyRequest, + requestStream: false, + responseType: Categories, + responseStream: false, + options: {}, + }, + getAllOrders: { + name: "GetAllOrders", + requestType: EmptyRequest, + requestStream: false, + responseType: Orders, + responseStream: false, + options: {}, + }, + getAllUserOrders: { + name: "GetAllUserOrders", + requestType: UserRequest, + requestStream: false, + responseType: Orders, + responseStream: false, + options: {}, + }, + getOrder: { + name: "GetOrder", + requestType: OrderRequest, + requestStream: false, + responseType: Order, + responseStream: false, + options: {}, + }, + getUser: { + name: "GetUser", + requestType: UserRequest, + requestStream: false, + responseType: User, + responseStream: false, + options: {}, + }, + getAllUsers: { + name: "GetAllUsers", + requestType: EmptyRequest, + requestStream: false, + responseType: Users, + responseStream: false, + options: {}, + }, + postOrder: { + name: "PostOrder", + requestType: Order, + requestStream: false, + responseType: Order, + responseStream: false, + options: {}, + }, + patchAccountDetails: { + name: "PatchAccountDetails", + requestType: UserPatchRequest, + requestStream: false, + responseType: User, + responseStream: false, + options: {}, + }, + }, +} as const; + +export interface AppServiceImplementation<CallContextExt = {}> { + getProduct(request: ProductRequest, context: CallContext & CallContextExt): Promise<DeepPartial<Product>>; + getRandomProduct(request: EmptyRequest, context: CallContext & CallContextExt): Promise<DeepPartial<Product>>; + getAllProducts(request: AllProductsRequest, context: CallContext & CallContextExt): Promise<DeepPartial<Products>>; + getAllCategories(request: EmptyRequest, context: CallContext & CallContextExt): Promise<DeepPartial<Categories>>; + getAllOrders(request: EmptyRequest, context: CallContext & CallContextExt): Promise<DeepPartial<Orders>>; + getAllUserOrders(request: UserRequest, context: CallContext & CallContextExt): Promise<DeepPartial<Orders>>; + getOrder(request: OrderRequest, context: CallContext & CallContextExt): Promise<DeepPartial<Order>>; + getUser(request: UserRequest, context: CallContext & CallContextExt): Promise<DeepPartial<User>>; + getAllUsers(request: EmptyRequest, context: CallContext & CallContextExt): Promise<DeepPartial<Users>>; + postOrder(request: Order, context: CallContext & CallContextExt): Promise<DeepPartial<Order>>; + patchAccountDetails(request: UserPatchRequest, context: CallContext & CallContextExt): Promise<DeepPartial<User>>; +} + +export interface AppClient<CallOptionsExt = {}> { + getProduct(request: DeepPartial<ProductRequest>, options?: CallOptions & CallOptionsExt): Promise<Product>; + getRandomProduct(request: DeepPartial<EmptyRequest>, options?: CallOptions & CallOptionsExt): Promise<Product>; + getAllProducts(request: DeepPartial<AllProductsRequest>, options?: CallOptions & CallOptionsExt): Promise<Products>; + getAllCategories(request: DeepPartial<EmptyRequest>, options?: CallOptions & CallOptionsExt): Promise<Categories>; + getAllOrders(request: DeepPartial<EmptyRequest>, options?: CallOptions & CallOptionsExt): Promise<Orders>; + getAllUserOrders(request: DeepPartial<UserRequest>, options?: CallOptions & CallOptionsExt): Promise<Orders>; + getOrder(request: DeepPartial<OrderRequest>, options?: CallOptions & CallOptionsExt): Promise<Order>; + getUser(request: DeepPartial<UserRequest>, options?: CallOptions & CallOptionsExt): Promise<User>; + getAllUsers(request: DeepPartial<EmptyRequest>, options?: CallOptions & CallOptionsExt): Promise<Users>; + postOrder(request: DeepPartial<Order>, options?: CallOptions & CallOptionsExt): Promise<Order>; + patchAccountDetails(request: DeepPartial<UserPatchRequest>, options?: CallOptions & CallOptionsExt): Promise<User>; +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial<T> = T extends Builtin ? T + : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>> + : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> + : T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> } + : Partial<T>; + +function longToNumber(long: Long): number { + if (long.gt(globalThis.Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000000000000000000000000000000000000..c662cb677bd9f09c87dc722011ef1ea77add850f --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,33 @@ +const constants = { + TEST_USER_ID: "5b4b67e2-3b09-4309-8de2-82cc469fc1cc", + TEST_PRODUCT_ID: "dc878081-060b-4a6c-9038-02069f1ecef8", + TEST_ORDER_ID: "f38c26bf-8583-4e6f-88e9-0492490e47be", + TEST_ORDER: { + userId: "05843a96-1d34-43fb-88ca-0567e2f39ef6", + products: [{ productId: "0d00fb9d-0733-4c57-956f-85d52c1d14ee", quantity: 1 }], + totalAmount: 8 + }, + TEST_UPDATE: { + id: "05843a96-1d34-43fb-88ca-0567e2f39ef6", + password: "test123" + }, + + EXPECTED_USER: { + id: "5b4b67e2-3b09-4309-8de2-82cc469fc1cc", + email: "JamesWilliams@gmail.com", + }, + EXPECTED_PRODUCT: { + id: "dc878081-060b-4a6c-9038-02069f1ecef8", + stock: 167, + price: 12, + name: "Shoes", + description: "Shoes for sale", + categoryId: "44adf4f9-cdd3-49ba-9788-eaed531580de" + }, + EXPECTED_ORDER: { + id: "f38c26bf-8583-4e6f-88e9-0492490e47be", + products: [{ "productId": "d3e11810-8b5a-4468-a781-84109dd7569a", "quantity": 2 }] + }, +}; + +export default constants; \ No newline at end of file diff --git a/src/interfaces.ts b/src/interfaces.ts new file mode 100644 index 0000000000000000000000000000000000000000..df0f3d35995ee390d9481262737dc82f11299541 --- /dev/null +++ b/src/interfaces.ts @@ -0,0 +1,17 @@ +import { UserPatchRequest } from "./compiled_proto/app"; +import { Category, Order, Product, TestResults, User } from "./types"; + +export interface ITestSuite { + testRandomProduct(): Promise<Product>; + testUserById(id: string): Promise<User>; + testAllProducts(categoryId?: string): Promise<Product[]>; + testProductById(productId: string): Promise<Product>; + testAllCategories(): Promise<Category[]>; + testAllOrders(): Promise<Order[]>; + testOrdersByUser(id: string): Promise<Order[]>; + testOrderById(id: string): Promise<Order>; + testAllUsers(): Promise<User[]>; + testInsertOrder(order: Order): Promise<void>; + updateUser(patch: UserPatchRequest): Promise<void>; + runSuite(iterations: number): Promise<TestResults>; +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000000000000000000000000000000000000..2b9802ff828363d6d224b42ed6c53a9e54408e64 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,43 @@ +import express from 'express'; +import expressWs from 'express-ws'; +import path from 'path'; +import { runTests } from './testing'; + +const { app } = expressWs(express()); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); + +const resultsCache = {}; + +app.get('/', (req, res) => { + res.sendFile(path.join(__dirname, 'index.html')); +}); + +app.post('/viewResults', (req, res) => { + const url = req.body.url; + const id = btoa(url); + res.redirect(`/viewResults/${id}`); +}); + +app.get('/viewResults/:id', (req, res) => { + res.sendFile(path.join(__dirname, 'results.html')); +}); + +app.ws('/results/:id', async (ws, req) => { + const awsUrl = atob(req.params.id); + const iterations: number = Number(req.query.iterations) || 1; + if (resultsCache[awsUrl]) { + ws.send(JSON.stringify(resultsCache[awsUrl])); + } else { + runTests(awsUrl, resultsCache, iterations, ws); + } + + ws.onmessage = (msg) => { + const iterations = Number(msg.data); + runTests(awsUrl, resultsCache, iterations, ws); + }; +}); + +app.listen(80, () => { + console.log('Server is running on port 80'); +}); diff --git a/app.proto b/src/proto/app.proto similarity index 92% rename from app.proto rename to src/proto/app.proto index 98ff9b97eff6d0ffc36c7859208e93a205db2b96..f223b1646d677346864f9f6b20002ee7c8876f72 100644 --- a/app.proto +++ b/src/proto/app.proto @@ -5,7 +5,7 @@ service App { rpc GetRandomProduct (EmptyRequest) returns (Product) {} - rpc GetAllProducts (EmptyRequest) returns (Products) {} + rpc GetAllProducts (AllProductsRequest) returns (Products) {} rpc GetAllCategories (EmptyRequest) returns (Categories) {} @@ -28,13 +28,16 @@ message EmptyRequest {} // Products +message AllProductsRequest { + optional string category_id = 1; +} + message ProductRequest { string product_id = 1; } message Product { string category_id = 1; - string images = 2; int64 stock = 3; int64 price = 4; string description = 5; diff --git a/src/testing.ts b/src/testing.ts new file mode 100644 index 0000000000000000000000000000000000000000..4971c74a78f3eed43ad2d5f15e46f1607b757ada --- /dev/null +++ b/src/testing.ts @@ -0,0 +1,44 @@ +import RestTestSuite from "./tests/restTests"; +import GrpcTestSuite from "./tests/grpcTests"; +import { performance } from "perf_hooks"; +import { TestResult } from "./types"; + +const runTest: (func: () => any, expected?: any) => Promise<TestResult> = async (func: () => any, expected?: any) => { + const start = performance.now(); + const result = await func(); + const end = performance.now(); + + return { + ok: expected != null ? JSON.stringify(result) === JSON.stringify(expected) : true, + time: end - start + }; +} + +export const avgRuntime: (func: () => any, iterations: number, expected?: any) => Promise<TestResult> = async (func: () => any, iterations: number, expected?: any) => { + const promises = []; + for (let i = 0; i < iterations; i++) { + promises.push(runTest(func, expected)); + } + + const results = await Promise.all(promises); + return { + ok: results.reduce((acc, curr) => acc && curr.ok, true), + time: results.reduce((acc, curr) => acc + curr.time, 0) / iterations + }; +}; + + +export async function runTests(awsUrl: string, resultCache: any, iterations: number, ws?: any) { + const restTestSuite = new RestTestSuite(awsUrl); + const grpcTestSuite = new GrpcTestSuite(awsUrl); + + // Warmup + await restTestSuite.runSuite(1); + await grpcTestSuite.runSuite(1); + resultCache[awsUrl] = {}; + resultCache[awsUrl]["rest"] = await restTestSuite.runSuite(iterations); + resultCache[awsUrl]["grpc"] = await grpcTestSuite.runSuite(iterations); + + if (ws) + ws.send(JSON.stringify(resultCache[awsUrl])); +} diff --git a/src/tests/grpcTests.ts b/src/tests/grpcTests.ts new file mode 100644 index 0000000000000000000000000000000000000000..0fc200b5b6e52494935ca8cf4a8f9c6486ddf95a --- /dev/null +++ b/src/tests/grpcTests.ts @@ -0,0 +1,189 @@ +import { ITestSuite } from "../interfaces"; +import { AppClient, AppDefinition } from "../compiled_proto/app"; +import { createChannel, createClient } from 'nice-grpc'; +import { Order, TestResults, UserPatchRequest } from "../types"; +import { avgRuntime } from "../testing"; +import constants from "../constants"; +import { uuid } from "uuidv4"; + +export default class GrpcTestSuite implements ITestSuite { + private channel: any; + private client: AppClient; + + constructor(ip: string) { + this.channel = createChannel(`${ip}:3001`); + this.client = createClient(AppDefinition, this.channel); + } + + public async testRandomProduct(): Promise<any> { + try { + const data = await this.client.getRandomProduct({}); + return data; + } catch (e) { + console.error(e); + return null; + } + } + + public async testUserById(id: string): Promise<any> { + try { + const data = await this.client.getUser({ id }); + return data; + } catch (e) { + console.error(e); + return null; + } + } + + public async testAllProducts(categoryId?: string): Promise<any> { + try { + const data = await this.client.getAllProducts({ categoryId }); + return data; + } catch (e) { + console.error(e); + return null; + } + } + + public async testProductById(productId: string): Promise<any> { + try { + const data = await this.client.getProduct({ productId }); + return data; + } catch (e) { + console.error(e); + return null; + } + + } + + public async testAllCategories(): Promise<any> { + try { + const data = await this.client.getAllCategories({}); + return data; + } catch (e) { + console.error(e); + return null; + } + } + + public async testAllOrders(): Promise<any> { + try { + const data = await this.client.getAllOrders({}); + return data; + } catch (e) { + console.error(e); + return null; + } + } + + public async testOrdersByUser(id: string): Promise<any> { + try { + const data = await this.client.getAllUserOrders({ id }); + return data; + } catch (e) { + console.error(e); + return null; + } + } + + public async testOrderById(id: string): Promise<any> { + try { + const data = await this.client.getOrder({ id }); + return data; + } catch (e) { + console.error(e); + return null; + } + } + + public async testAllUsers(): Promise<any> { + try { + const data = await this.client.getAllUsers({}); + return data; + } catch (e) { + console.error(e); + return null; + } + } + + public async testInsertOrder(order: Order): Promise<any> { + try { + const data = await this.client.postOrder(order); + return data; + } catch (e) { + console.error(e); + return null; + } + } + + public async updateUser(patch: UserPatchRequest): Promise<any> { + try { + const data = await this.client.patchAccountDetails(patch); + return data; + } catch (e) { + console.error(e); + return null; + } + } + + public async runSuite(iterations: number): Promise<TestResults> { + const results: TestResults = { + randomProduct: { + ok: false, + time: 0 + }, + userById: { + ok: false, + time: 0 + }, + allProducts: { + ok: false, + time: 0 + }, + productById: { + ok: false, + time: 0 + }, + allCategories: { + ok: false, + time: 0 + }, + allOrders: { + ok: false, + time: 0 + }, + ordersByUser: { + ok: false, + time: 0 + }, + orderById: { + ok: false, + time: 0 + }, + allUsers: { + ok: false, + time: 0 + }, + insertOrder: { + ok: false, + time: 0 + }, + updateUser: { + ok: false, + time: 0 + } + }; + results.randomProduct = await avgRuntime(async () => await this.testRandomProduct(), iterations); + results.userById = await avgRuntime(async () => await this.testUserById(constants.TEST_USER_ID), iterations, constants.EXPECTED_USER); + results.allProducts = await avgRuntime(async () => await this.testAllProducts(), iterations); + results.productById = await avgRuntime(async () => await this.testProductById(constants.TEST_PRODUCT_ID), iterations, constants.EXPECTED_PRODUCT); + results.allCategories = await avgRuntime(async () => await this.testAllCategories(), iterations); + results.allOrders = await avgRuntime(async () => await this.testAllOrders(), iterations); + results.ordersByUser = await avgRuntime(async () => await this.testOrdersByUser(constants.TEST_USER_ID), iterations); + results.orderById = await avgRuntime(async () => await this.testOrderById(constants.TEST_ORDER_ID), iterations, constants.EXPECTED_ORDER); + results.allUsers = await avgRuntime(async () => await this.testAllUsers(), iterations); + results.insertOrder = await avgRuntime(async () => await this.testInsertOrder({ id: uuid(), ...constants.TEST_ORDER }), iterations); + results.updateUser = await avgRuntime(async () => await this.updateUser(constants.TEST_UPDATE), iterations); + return results; + } +} \ No newline at end of file diff --git a/src/tests/restTests.ts b/src/tests/restTests.ts new file mode 100644 index 0000000000000000000000000000000000000000..1e96ae176a6f981f8e531f1e817af464a685e127 --- /dev/null +++ b/src/tests/restTests.ts @@ -0,0 +1,213 @@ +import { ITestSuite } from '../interfaces'; +import { Category, Order, Product, TestResults, User, UserPatchRequest } from '../types'; +import { avgRuntime } from '../testing'; +import { uuid } from 'uuidv4'; +import constants from '../constants'; + +export default class RestTestSuite implements ITestSuite { + private endpoint: string; + + constructor(ip: string) { + this.endpoint = `http://${ip}:3000`; + } + + public async testRandomProduct(): Promise<Product> { + try { + const resp = await fetch(this.endpoint + "/randomproduct"); + if (!resp.ok) throw new Error("Failed to fetch random product"); + const json = await resp.json(); + return json; + } catch (e) { + console.error(e); + return null; + } + } + + public async testUserById(id: string): Promise<User> { + try { + const resp = await fetch(this.endpoint + "/user/" + id); + if (!resp.ok) throw new Error("Failed to fetch user by id"); + const json = await resp.json(); + return json; + } catch (e) { + console.error("error"); + return null; + } + } + + public async testAllProducts(categoryId?: string): Promise<Product[]> { + try { + const resp = await fetch(this.endpoint + "/products" + (categoryId ? "?categoryId=" + categoryId : "")); + if (!resp.ok) throw new Error("Failed to fetch all products"); + const json = await resp.json(); + return json; + } catch (e) { + console.error(e); + return null; + } + } + + public async testProductById(productId: string): Promise<Product> { + try { + const resp = await fetch(this.endpoint + "/product/" + productId); + if (!resp.ok) throw new Error("Failed to fetch product by id"); + const json = await resp.json(); + return json; + } catch (e) { + console.error(e); + return null; + } + } + + public async testAllCategories(): Promise<Category[]> { + try { + const resp = await fetch(this.endpoint + "/categories"); + if (!resp.ok) throw new Error("Failed to fetch all categories"); + const json = await resp.json(); + return json; + } catch (e) { + console.error(e); + return null; + } + } + + public async testAllOrders(): Promise<Order[]> { + try { + const resp = await fetch(this.endpoint + "/allorders"); + if (!resp.ok) throw new Error("Failed to fetch all orders"); + const json = await resp.json(); + return json; + } catch (e) { + console.error(e); + return null; + } + } + + public async testOrdersByUser(id: string): Promise<Order[]> { + try { + const resp = await fetch(this.endpoint + "/orders?id=" + id); + if (!resp.ok) throw new Error("Failed to fetch orders by user"); + const json = await resp.json(); + return json; + } catch (e) { + console.error(e); + return null; + } + } + + public async testOrderById(id: string): Promise<Order> { + try { + const resp = await fetch(this.endpoint + "/order/" + id); + if (!resp.ok) throw new Error("Failed to fetch order by id"); + const json = await resp.json(); + return json; + } catch (e) { + console.error(e); + return null; + } + } + + public async testAllUsers(): Promise<User[]> { + try { + const resp = await fetch(this.endpoint + "/users"); + if (!resp.ok) throw new Error("Failed to fetch all users"); + const json = await resp.json(); + return json; + } catch (e) { + console.error(e); + return null; + } + } + + public async testInsertOrder(order: Order): Promise<void> { + try { + const resp = await fetch(this.endpoint + "/orders", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(order) + }); + if (!resp.ok) throw new Error("Failed to insert order"); + } catch (e) { + console.error(e); + } + } + + public async updateUser(patch: UserPatchRequest): Promise<void> { + try { + const resp = await fetch(this.endpoint + `/user/${patch.id}`, { + method: "PATCH", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(patch) + }); + if (!resp.ok) throw new Error("Failed to update user"); + } catch (e) { + console.error(e); + } + } + + public async runSuite(iterations: number): Promise<TestResults> { + const results: TestResults = { + randomProduct: { + ok: false, + time: 0 + }, + userById: { + ok: false, + time: 0 + }, + allProducts: { + ok: false, + time: 0 + }, + productById: { + ok: false, + time: 0 + }, + allCategories: { + ok: false, + time: 0 + }, + allOrders: { + ok: false, + time: 0 + }, + ordersByUser: { + ok: false, + time: 0 + }, + orderById: { + ok: false, + time: 0 + }, + allUsers: { + ok: false, + time: 0 + }, + insertOrder: { + ok: false, + time: 0 + }, + updateUser: { + ok: false, + time: 0 + } + }; + results.randomProduct = await avgRuntime(async () => await this.testRandomProduct(), iterations); + results.userById = await avgRuntime(async () => await this.testUserById(constants.TEST_USER_ID), iterations, constants.EXPECTED_USER); + results.allProducts = await avgRuntime(async () => await this.testAllProducts(), iterations); + results.productById = await avgRuntime(async () => await this.testProductById(constants.TEST_PRODUCT_ID), iterations, constants.EXPECTED_PRODUCT); + results.allCategories = await avgRuntime(async () => await this.testAllCategories(), iterations); + results.allOrders = await avgRuntime(async () => await this.testAllOrders(), iterations); + results.ordersByUser = await avgRuntime(async () => await this.testOrdersByUser(constants.TEST_USER_ID), iterations); + results.orderById = await avgRuntime(async () => await this.testOrderById(constants.TEST_ORDER_ID), iterations, constants.EXPECTED_ORDER); + results.allUsers = await avgRuntime(async () => await this.testAllUsers(), iterations); + results.insertOrder = await avgRuntime(async () => await this.testInsertOrder({ id: uuid(), ...constants.TEST_ORDER }), iterations); + results.updateUser = await avgRuntime(async () => await this.updateUser(constants.TEST_UPDATE), iterations); + + return results; + } +} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..90a277511149f3a11f53896119c71cbf82d40a09 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,73 @@ +export type ProductRequest = { + productId: string; +}; + +export type AllProductsRequest = { + categoryId: string; +} + +export type Product = { + categoryId: string; + stock: number; + price: number; + description: string; + id: string; + name: string; +}; + +export type Order = { + userId: string; + id: string; + products: OrderProduct[]; + totalAmount: number; +}; + +export type OrderRequest = { + id: string; +} + +export type OrderProduct = { + productId: string; + quantity: number; +}; + +export type Category = { + description: string; + id: string; + price: number; + name: string; +}; + +export type UserRequest = { + id: string; +}; + +export type UserPatchRequest = { + id: string; + email?: string; + password?: string; +} + +export type User = { + id: string; + email: string; +} + +export type TestResult = { + ok: boolean; + time: number; +} + +export type TestResults = { + randomProduct: TestResult; + userById: TestResult; + allProducts: TestResult; + productById: TestResult; + allCategories: TestResult; + allOrders: TestResult; + ordersByUser: TestResult; + orderById: TestResult; + allUsers: TestResult; + insertOrder: TestResult; + updateUser: TestResult; +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..e5a4f8f9000664f93c457dbecc4618dcdfd1c78e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "module": "commonjs", + "esModuleInterop": true, + "target": "es6", + "moduleResolution": "node", + "sourceMap": true, + "outDir": "dist", + }, + "include": [ + "src/**/*" + ], + "lib": [ + "es2015" + ] +} \ No newline at end of file