diff --git a/package-lock.json b/package-lock.json index ba4fcfb58..5d88ff12f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,6 @@ "protobufjs": "^7.3.2", "pureimage": "^0.4.13", "raphael": "^2.3.0", - "typia": "^6.5.2", "uuid": "^10.0.0", "wheelnav": "^1.7.1", "ws": "^8.18.0", @@ -43,6 +42,7 @@ "@types/chai": "^4.3.17", "@types/d3": "^7.4.3", "@types/jest": "^29.5.12", + "@types/jquery": "^3.5.31", "@types/mocha": "^10.0.7", "@types/node": "^22.7.5", "@types/sinon": "^17.0.3", @@ -3775,11 +3775,6 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", "license": "BSD-3-Clause" }, - "node_modules/@samchon/openapi": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@samchon/openapi/-/openapi-0.4.2.tgz", - "integrity": "sha512-lpL+HExpCH65EA8M4L0+bcC0T+0MQCaXjaHdmQ6oHQeSjTgS7ZyoA3l2B6FrkF6XA+mWosQhCmXH5t+VqmoGUQ==" - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -4356,6 +4351,16 @@ "jimp": "*" } }, + "node_modules/@types/jquery": { + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.31.tgz", + "integrity": "sha512-rf/iB+cPJ/YZfMwr+FVuQbm7IaWC4y3FVYfVDxRGqmUCFjjPII0HWaP0vTPJGp6m4o13AXySCcMbWfrWtBFAKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sizzle": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -4542,6 +4547,13 @@ "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", "dev": true }, + "node_modules/@types/sizzle": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", + "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/sockjs": { "version": "0.3.36", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", @@ -4986,6 +4998,7 @@ "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, "dependencies": { "type-fest": "^0.21.3" }, @@ -5065,11 +5078,6 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, - "node_modules/array-timsort": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", - "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==" - }, "node_modules/arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -5372,16 +5380,6 @@ "resolved": "https://registry.npmjs.org/binary-loader/-/binary-loader-0.0.1.tgz", "integrity": "sha512-LujAJL3IhYn4zVWZWAct9B6G5hdonNle+fWsG/u9QyY1OhOVp00jFgygUVr9SrBVw8doddyx9A/VS1/T9co4Dg==" }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/bmp-js": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", @@ -5702,6 +5700,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -5723,11 +5722,6 @@ "node": ">=10" } }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, "node_modules/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -5830,36 +5824,6 @@ "node": ">=6" } }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "engines": { - "node": ">= 10" - } - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -5874,14 +5838,6 @@ "node": ">=12" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" - } - }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -5947,21 +5903,6 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "node_modules/comment-json": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.4.tgz", - "integrity": "sha512-E5AjpSW+O+N5T2GsOQMHLLsJvrYw6G/AFt9GvU6NguEAfzKShh7hRiLtVo6S9KbRpFMGqE5ojo0/hE+sdteWvQ==", - "dependencies": { - "array-timsort": "^1.0.3", - "core-util-is": "^1.0.3", - "esprima": "^4.0.1", - "has-own-prop": "^2.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -6122,7 +6063,8 @@ "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true }, "node_modules/create-jest": { "version": "29.7.0", @@ -6781,17 +6723,6 @@ "node": ">= 10" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -6974,14 +6905,6 @@ "tslib": "^2.0.3" } }, - "node_modules/drange": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz", - "integrity": "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==", - "engines": { - "node": ">=4" - } - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -7223,6 +7146,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -7244,6 +7168,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -7436,19 +7361,6 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7498,20 +7410,6 @@ "bser": "2.1.1" } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/file-loader": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", @@ -8021,14 +7919,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-own-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", - "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", + "dev": true, "engines": { "node": ">=8" } @@ -8574,44 +8465,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/inquirer/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/internmap": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", @@ -8758,14 +8611,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "engines": { - "node": ">=8" - } - }, "node_modules/is-lambda": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", @@ -8831,6 +8676,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, "engines": { "node": ">=10" }, @@ -10097,7 +9943,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -10123,6 +9970,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -10322,6 +10170,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, "engines": { "node": ">=6" } @@ -10803,11 +10652,6 @@ "multicast-dns": "cli.js" } }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -11075,6 +10919,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -11112,36 +10957,6 @@ "ot": "bin/ot" } }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -11787,18 +11602,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/randexp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", - "integrity": "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==", - "dependencies": { - "drange": "^1.0.2", - "ret": "^0.2.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -12018,14 +11821,6 @@ "strip-ansi": "^6.0.1" } }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "engines": { - "node": ">=0.10" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -12108,26 +11903,6 @@ "node": ">=10" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", - "engines": { - "node": ">=4" - } - }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -12173,14 +11948,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/rw": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", @@ -12191,6 +11958,7 @@ "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, "dependencies": { "tslib": "^2.1.0" } @@ -12483,7 +12251,8 @@ "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==" + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true }, "node_modules/sinon": { "version": "18.0.0", @@ -12923,6 +12692,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -13122,11 +12892,6 @@ "tslib": "^2" } }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -13143,17 +12908,6 @@ "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -13467,7 +13221,8 @@ "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true }, "node_modules/tsx": { "version": "4.17.0", @@ -13502,6 +13257,7 @@ "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, "engines": { "node": ">=10" }, @@ -13526,6 +13282,7 @@ "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -13535,32 +13292,6 @@ "node": ">=14.17" } }, - "node_modules/typia": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/typia/-/typia-6.5.2.tgz", - "integrity": "sha512-s1yqB7m8Zz4yoeLdq1OQ7Nq/svHqOr02fl9B3dOiU7m+4p5wHvJVTj7gLRtq0t51ExFUlDzSkY9STwBUNACOgA==", - "dependencies": { - "@samchon/openapi": "^0.4.2", - "commander": "^10.0.0", - "comment-json": "^4.2.3", - "inquirer": "^8.2.5", - "randexp": "^0.5.3" - }, - "bin": { - "typia": "lib/executable/typia.js" - }, - "peerDependencies": { - "typescript": ">=4.8.0 <5.6.0" - } - }, - "node_modules/typia/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "engines": { - "node": ">=14" - } - }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", @@ -13789,14 +13520,6 @@ "minimalistic-assert": "^1.0.0" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index 033f7c891..aa5fd40f2 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@types/chai": "^4.3.17", "@types/d3": "^7.4.3", "@types/jest": "^29.5.12", + "@types/jquery": "^3.5.31", "@types/mocha": "^10.0.7", "@types/node": "^22.7.5", "@types/sinon": "^17.0.3", @@ -76,7 +77,6 @@ "protobufjs": "^7.3.2", "pureimage": "^0.4.13", "raphael": "^2.3.0", - "typia": "^6.5.2", "uuid": "^10.0.0", "wheelnav": "^1.7.1", "ws": "^8.18.0", diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts new file mode 100644 index 000000000..76e8a2c6f --- /dev/null +++ b/src/client/HostLobbyModal.ts @@ -0,0 +1,211 @@ +import {LitElement, html, css} from 'lit'; +import {customElement, property, state} from 'lit/decorators.js'; +import {GameMap} from '../core/game/Game'; +import {Lobby} from '../core/Schemas'; + +@customElement('host-lobby-modal') +export class HostLobbyModal extends LitElement { + @state() private isModalOpen = false; + @state() private selectedMap: GameMap = GameMap.World; + @state() private lobbyId = 'a345d'; + @state() private copySuccess = false; + + static styles = css` + .modal-overlay { + display: none; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + } + + .modal-content { + background-color: white; + margin: 15% auto; + padding: 20px; + border-radius: 8px; + width: 80%; + max-width: 500px; + text-align: center; + } + + .close { + color: #aaa; + float: right; + font-size: 28px; + font-weight: bold; + cursor: pointer; + } + + .close:hover, + .close:focus { + color: black; + text-decoration: none; + cursor: pointer; + } + + button { + padding: 10px 20px; + font-size: 16px; + cursor: pointer; + background-color: #007bff; + color: white; + border: none; + border-radius: 4px; + transition: background-color 0.3s; + display: inline-block; + margin-top: 20px; + } + + button:hover { + background-color: #0056b3; + } + + select { + padding: 8px; + font-size: 16px; + margin-top: 10px; + width: 200px; + } + + .lobby-id-container { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + } + + .clipboard-icon { + cursor: pointer; + transition: opacity 0.3s; + } + + .clipboard-icon:hover { + opacity: 0.7; + } + + .copy-success { + color: green; + font-size: 14px; + margin-top: 5px; + } + `; + + render() { + return html` + + `; + } + + public open() { + createLobby().then((lobby) => { + this.lobbyId = lobby.id + // join lobby + }).then(() => { + this.dispatchEvent(new CustomEvent('join-lobby', { + detail: { + singlePlayer: false, + lobby: { + id: this.lobbyId, + }, + map: this.selectedMap, + }, + bubbles: true, + composed: true + })); + + }) + this.isModalOpen = true; + } + + public close() { + this.isModalOpen = false; + this.copySuccess = false; + } + + private handleMapChange(e: Event) { + this.selectedMap = Number((e.target as HTMLSelectElement).value) as GameMap; + } + private async startGame() { + console.log(`Starting single player game with map: ${GameMap[this.selectedMap]}`); + this.close(); + const response = await fetch(`/start_private_lobby/${this.lobbyId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + } + }); + } + + private async copyToClipboard() { + try { + await navigator.clipboard.writeText(this.lobbyId); + this.copySuccess = true; + setTimeout(() => { + this.copySuccess = false; + }, 2000); + } catch (err) { + console.error('Failed to copy text: ', err); + } + } + +} + +async function createLobby(): Promise { + try { + const response = await fetch('/private_lobby', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + // body: JSON.stringify(data), // Include this if you need to send data + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + console.log('Success:', data); + + // Assuming the server returns an object with an 'id' property + const lobby: Lobby = { + id: data.id, + // Add other properties as needed + }; + + return lobby; + } catch (error) { + console.error('Error creating lobby:', error); + throw error; // Re-throw the error so the caller can handle it + } +} \ No newline at end of file diff --git a/src/client/JoinPrivateLobbyModal.ts b/src/client/JoinPrivateLobbyModal.ts new file mode 100644 index 000000000..4d81396b4 --- /dev/null +++ b/src/client/JoinPrivateLobbyModal.ts @@ -0,0 +1,128 @@ +import {LitElement, html, css} from 'lit'; +import {customElement, property, state, query} from 'lit/decorators.js'; +import {GameMap} from '../core/game/Game'; + +@customElement('join-private-lobby-modal') +export class JoinPrivateLobbyModal extends LitElement { + @state() private isModalOpen = false; + @query('#lobbyIdInput') private lobbyIdInput!: HTMLInputElement; + + static styles = css` + .modal-overlay { + display: none; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + } + .modal-content { + background-color: white; + margin: 15% auto; + padding: 20px; + border-radius: 8px; + width: 80%; + max-width: 500px; + text-align: center; + } + .close { + color: #aaa; + float: right; + font-size: 28px; + font-weight: bold; + cursor: pointer; + } + .close:hover, + .close:focus { + color: black; + text-decoration: none; + cursor: pointer; + } + button { + padding: 10px 20px; + font-size: 16px; + cursor: pointer; + background-color: #007bff; + color: white; + border: none; + border-radius: 4px; + transition: background-color 0.3s; + } + button:hover { + background-color: #0056b3; + } + .lobby-id-container { + display: flex; + align-items: stretch; + justify-content: center; + gap: 10px; + margin: 20px 0; + } + .lobby-id-container input { + flex-grow: 1; + max-width: 200px; + padding: 10px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 4px; + } + .lobby-id-container button { + padding: 10px 15px; + } + .join-button { + margin-top: 10px; + } + `; + + render() { + return html` + + `; + } + + public open() { + this.isModalOpen = true; + } + + public close() { + this.isModalOpen = false; + } + + private async pasteFromClipboard() { + try { + const clipText = await navigator.clipboard.readText(); + this.lobbyIdInput.value = clipText; + } catch (err) { + console.error('Failed to read clipboard contents: ', err); + } + } + + private joinLobby() { + const lobbyId = this.lobbyIdInput.value; + // Add your logic here to join the lobby using the lobbyId + console.log(`Joining lobby with ID: ${lobbyId}`); + this.dispatchEvent(new CustomEvent('join-lobby', { + detail: { + lobby: {id: lobbyId}, + singlePlayer: false, + map: GameMap.World, + }, + bubbles: true, + composed: true + })) + + this.close(); + } +} \ No newline at end of file diff --git a/src/client/Main.ts b/src/client/Main.ts index 693731a4a..de2c5a86b 100644 --- a/src/client/Main.ts +++ b/src/client/Main.ts @@ -4,15 +4,13 @@ import favicon from '../../resources/images/Favicon.png'; import './PublicLobby'; import './UsernameInput'; - - import './styles.css'; import {UsernameInput} from "./UsernameInput"; import {SinglePlayerModal} from "./SinglePlayerModal"; -import {GameMap} from "../core/game/Game"; +import {HostLobbyModal as HostPrivateLobbyModal} from "./HostLobbyModal"; +import {JoinPrivateLobbyModal} from "./JoinPrivateLobbyModal"; -const usernameKey: string = 'username'; class Client { @@ -39,14 +37,24 @@ class Client { document.addEventListener('single-player', this.handleSinglePlayer.bind(this)); - const singlePlayerButton = document.getElementById('single-player'); - const modal = document.querySelector('single-player-modal') as SinglePlayerModal; + const spModal = document.querySelector('single-player-modal') as SinglePlayerModal; + spModal instanceof SinglePlayerModal + document.getElementById('single-player').addEventListener('click', () => { + spModal.open(); + }) + + const hostModal = document.querySelector('host-lobby-modal') as HostPrivateLobbyModal; + hostModal instanceof HostPrivateLobbyModal + document.getElementById('host-lobby-button').addEventListener('click', () => { + hostModal.open(); + }) + + const joinModal = document.querySelector('join-private-lobby-modal') as JoinPrivateLobbyModal; + joinModal instanceof JoinPrivateLobbyModal + document.getElementById('join-private-lobby-button').addEventListener('click', () => { + joinModal.open(); + }) - if (singlePlayerButton && modal instanceof SinglePlayerModal) { - singlePlayerButton.addEventListener('click', () => { - modal.open(); - }); - } } diff --git a/src/client/PublicLobby.ts b/src/client/PublicLobby.ts index f812e796f..30e8610a0 100644 --- a/src/client/PublicLobby.ts +++ b/src/client/PublicLobby.ts @@ -126,4 +126,4 @@ export class PublicLobby extends LitElement { this.currLobby = null } } -} \ No newline at end of file +} diff --git a/src/client/index.html b/src/client/index.html index d0ec86595..61b927634 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -42,11 +42,11 @@
- - @@ -68,6 +68,8 @@
+ + diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index 575c4b197..75cd84eab 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -45,8 +45,8 @@ const PlayerTypeSchema = z.nativeEnum(PlayerType); export interface Lobby { id: string; - msUntilStart: number; - numClients: number; + msUntilStart?: number; + numClients?: number; } const EmojiSchema = z.string().refine( diff --git a/src/server/GameManager.ts b/src/server/GameManager.ts index b9fee3eb4..ed39b5093 100644 --- a/src/server/GameManager.ts +++ b/src/server/GameManager.ts @@ -1,6 +1,4 @@ import {Config} from "../core/configuration/Config"; -import {PseudoRandom} from "../core/PseudoRandom"; -import WebSocket from 'ws'; import {ClientID, GameID} from "../core/Schemas"; import {v4 as uuidv4} from 'uuid'; import {Client} from "./Client"; @@ -14,8 +12,6 @@ export class GameManager { private games: GameServer[] = [] - private random = new PseudoRandom(123) - constructor(private config: Config) { } gamesByPhase(phase: GamePhase): GameServer[] { @@ -31,6 +27,23 @@ export class GameManager { game.addClient(client, lastTurn) } + createPrivateGame(): string { + const id = genSmallGameID() + this.games.push(new GameServer(id, Date.now(), false, this.config)) + return id + } + + // TODO: stop private games to prevent memory leak. + startPrivateGame(gameID: GameID) { + const game = this.games.find(g => g.id == gameID) + console.log(`found game ${game}`) + if (game) { + game.start() + } else { + throw new Error(`cannot start private game, game ${gameID} not found`) + } + } + tick() { const lobbies = this.gamesByPhase(GamePhase.Lobby) const active = this.gamesByPhase(GamePhase.Active) @@ -40,10 +53,10 @@ export class GameManager { if (now > this.lastNewLobby + this.config.gameCreationRate()) { this.lastNewLobby = now const id = uuidv4() - lobbies.push(new GameServer(id, now, this.config)) + lobbies.push(new GameServer(id, now, true, this.config)) } - active.filter(g => !g.hasStarted()).forEach(g => { + active.filter(g => !g.hasStarted() && g.isPublic).forEach(g => { g.start() }) finished.forEach(g => { @@ -51,4 +64,15 @@ export class GameManager { }) this.games = [...lobbies, ...active] } +} + +function genSmallGameID(): string { + // Generate a UUID + const uuid: string = uuidv4(); + + // Convert UUID to base64 + const base64: string = btoa(uuid); + + // Take the first 4 characters of the base64 string + return base64.slice(0, 4); } \ No newline at end of file diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index bc3f9b528..d915cbf54 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -26,6 +26,7 @@ export class GameServer { constructor( public readonly id: string, public readonly createdAt: number, + public readonly isPublic: boolean, private config: Config, ) { } @@ -129,6 +130,22 @@ export class GameServer { } phase(): GamePhase { + if (Date.now() > this.createdAt + this.config.lobbyLifetime() + this.maxGameDuration) { + console.warn(`game past max duration ${this.id}`) + return GamePhase.Finished + } + if (!this.isPublic) { + if (this._hasStarted) { + if (this.clients.length == 0) { + return GamePhase.Finished + } else { + return GamePhase.Active + } + } else { + return GamePhase.Lobby + } + } + if (Date.now() - this.createdAt < this.config.lobbyLifetime()) { return GamePhase.Lobby } @@ -137,11 +154,6 @@ export class GameServer { return GamePhase.Finished } - if (Date.now() > this.createdAt + this.config.lobbyLifetime() + this.maxGameDuration) { - console.warn(`game past max duration ${this.id}`) - return GamePhase.Finished - } - return GamePhase.Active } diff --git a/src/server/Server.ts b/src/server/Server.ts index 8a6267d7d..b4b3818d6 100644 --- a/src/server/Server.ts +++ b/src/server/Server.ts @@ -8,7 +8,8 @@ import {ClientMessage, ClientMessageSchema} from '../core/Schemas'; import {getConfig} from '../core/configuration/Config'; import {LogSeverity, slog} from './StructuredLog'; import {Client} from './Client'; -import {GamePhase} from './GameServer'; +import {GamePhase, GameServer} from './GameServer'; +import {v4 as uuidv4} from 'uuid'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -22,6 +23,8 @@ app.use(express.static(path.join(__dirname, '../../out'))); app.use(express.json()) const gm = new GameManager(getConfig()) +const privateGames = new Map() + // New GET endpoint to list lobbies app.get('/lobbies', (req, res) => { const now = Date.now() @@ -32,6 +35,30 @@ app.get('/lobbies', (req, res) => { }); }); +app.post('/private_lobby', (req, res) => { + const id = gm.createPrivateGame() + console.log('creating private lobby with id ${id}') + res.json({ + id: id + }); +}); + +app.post('/start_private_lobby/:id', (req, res) => { + console.log(`starting private lobby with id ${req.params.id}`) + gm.startPrivateGame(req.params.id) +}); + + +app.put('/private_lobby/:id', (req, res) => { + +}); + +app.get('/private_lobby/:id', (req, res) => { + res.json({ + hi: '5' + }); +}); + wss.on('connection', (ws) => { ws.on('message', (message: string) => { @@ -61,4 +88,3 @@ server.listen(PORT, () => { }); runGame() - diff --git a/webpack.config.js b/webpack.config.js index 8deef9255..38c06bdb0 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -95,7 +95,7 @@ export default (env, argv) => { ws: true, }, { - context: ['/lobbies', '/join_game', '/join_lobby'], + context: ['/lobbies', '/join_game', '/join_lobby', '/private_lobby', '/start_private_lobby'], target: 'http://localhost:3000', secure: false, changeOrigin: true,