diff --git a/client/Dockerfile b/client/Dockerfile index e55360b2..297ddadf 100644 --- a/client/Dockerfile +++ b/client/Dockerfile @@ -1,4 +1,4 @@ -ARG NODE_VERSION=17 +ARG NODE_VERSION=18 ARG MODE=production ARG BUILD_DATE ARG VCS_REF diff --git a/client/Dockerfile.dev b/client/Dockerfile.dev index 9141fe8a..530f6d5c 100644 --- a/client/Dockerfile.dev +++ b/client/Dockerfile.dev @@ -1,4 +1,4 @@ -ARG NODE_VERSION=17 +ARG NODE_VERSION=18 FROM node:${NODE_VERSION}-alpine diff --git a/client/package-lock.json b/client/package-lock.json index 9fd216ce..e6652e27 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -34,18 +34,17 @@ "@types/chromecast-caf-sender": "^1.0.3", "ajv": "^8.8.2", "app-manifest-loader": "^2.4.1", - "chromecast-device-emulator": "^1.2.7", + "chromecast-device-emulator": "^1.1.2", "compression-webpack-plugin": "^10.0.0", "css-hot-loader": "^1.4.4", "css-loader": "^6.3.0", "cssnano": "^5.0.5", "elm": "^0.19.1-3", "elm-analyse": "^0.16.5", - "elm-hot-webpack-loader": "^1.1.7", "elm-webpack-loader": "^8.0.0", "extract-loader": "^5.1.0", - "fibers": "^5.0.0", "file-loader": "^6.2.0", + "html-loader": "^3.1.0", "html-webpack-plugin": "^5.3.1", "mini-css-extract-plugin": "^2.3.0", "postcss-import": "^14.0.0", @@ -59,7 +58,6 @@ "style-loader": "^3.3.0", "terser-webpack-plugin": "^5.0.3", "ts-loader": "^9.2.6", - "tsickle-loader": "^0.5.0", "typescript": "^4.1.3", "webpack": "^5.56.1", "webpack-cli": "^4.3.1", @@ -4283,32 +4281,12 @@ } }, "node_modules/chromecast-device-emulator": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/chromecast-device-emulator/-/chromecast-device-emulator-1.2.7.tgz", - "integrity": "sha512-A93dyExP1j/Ho/n5Sqrm3uqvzFBsI5jmFrJRnqXt6nHAKDvK/mg+722R3V+OaGieWXiZmgVpi4snF6PrnNpVhw==", - "dev": true, - "dependencies": { - "ajv": "^4.11.8", - "chalk": "^2.4.1", - "commander": "^2.15.1", - "ws": "^5.2.0" - }, - "bin": { - "cde": "dist/cli/index.js", - "chromecast-device-emulator": "dist/cli/index.js" - }, - "engines": { - "node": ">= 6.9.0" - } - }, - "node_modules/chromecast-device-emulator/node_modules/ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha512-I/bSHSNEcFFqXLf91nchoNB9D1Kie3QKcWdchYUaoIg1+1bdWDkdfdlvdIOJbi9U8xR0y+MWc5D+won9v95WlQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/chromecast-device-emulator/-/chromecast-device-emulator-1.1.2.tgz", + "integrity": "sha512-VNaYLHsUatBtXy7uhueaQfY8yVJ87l/CUrkuGbDSrLNtAPa3DZSeurw5csfjx9Z+bgfd5WFmHM4yfcAVriM4+w==", "dev": true, "dependencies": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" + "ws": "^2.3.1" } }, "node_modules/clean-css": { @@ -4346,16 +4324,6 @@ "node": ">=6" } }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -4768,12 +4736,12 @@ } }, "node_modules/cssnano": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.8.tgz", - "integrity": "sha512-5lma/yQlK+6eOHSUqNAS11b4/fbiuasoxmCHoVYxSg6lQsyX7bGGIqiLi4o3Pe2CrUTrgcD2udW7JIgzC2806g==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.9.tgz", + "integrity": "sha512-hctQHIIeDrfMjq0bQhoVmRVaSeNNOGxkvkKVOcKpJzLr09wlRrZWH4GaYudp0aszpW8wJeaO5/yBmID9n7DNCg==", "dev": true, "dependencies": { - "cssnano-preset-default": "^5.2.8", + "cssnano-preset-default": "^5.2.9", "lilconfig": "^2.0.3", "yaml": "^1.10.2" }, @@ -4789,9 +4757,9 @@ } }, "node_modules/cssnano-preset-default": { - "version": "5.2.8", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.8.tgz", - "integrity": "sha512-6xQXUhTAPupvib3KC0Gl0d1jIwGFcJyuWQiMcA6grprGdmIzt1cxG5z78VuZu6DRRS6qin6ETkQsH6ixxb/SQw==", + "version": "5.2.9", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.9.tgz", + "integrity": "sha512-/4qcQcAfFEg+gnXE5NxKmYJ9JcT+8S5SDuJCLYMDN8sM/ymZ+lgLXq5+ohx/7V2brUCkgW2OaoCzOdAN0zvhGw==", "dev": true, "dependencies": { "css-declaration-sorter": "^6.2.2", @@ -4803,7 +4771,7 @@ "postcss-discard-duplicates": "^5.1.0", "postcss-discard-empty": "^5.1.1", "postcss-discard-overridden": "^5.1.0", - "postcss-merge-longhand": "^5.1.4", + "postcss-merge-longhand": "^5.1.5", "postcss-merge-rules": "^5.1.1", "postcss-minify-font-values": "^5.1.0", "postcss-minify-gradients": "^5.1.1", @@ -4938,6 +4906,8 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, + "optional": true, + "peer": true, "bin": { "detect-libc": "bin/detect-libc.js" }, @@ -5209,24 +5179,6 @@ "elm-format": "bin/elm-format" } }, - "node_modules/elm-hot": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/elm-hot/-/elm-hot-1.1.6.tgz", - "integrity": "sha512-zYZJlfs7Gt4BdjA+D+857K+XAWzwwySJmXCgFpHW1dIEfaHSZCIPYPf7/jinZBLfKRkOAlKzI32AA84DY50g7Q==", - "dev": true - }, - "node_modules/elm-hot-webpack-loader": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/elm-hot-webpack-loader/-/elm-hot-webpack-loader-1.1.8.tgz", - "integrity": "sha512-QXggtyhzeg7ioV8Ujoc6elFY+QYr49hFEb5EDcZP7976w62vjfqFX8YTa73alA+ayLT00ZsIU6eFjAzvLUzF0g==", - "dev": true, - "dependencies": { - "elm-hot": "^1.1.6" - }, - "peerDependencies": { - "elm-webpack-loader": "^7.0.1" - } - }, "node_modules/elm-webpack-loader": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/elm-webpack-loader/-/elm-webpack-loader-8.0.0.tgz", @@ -5764,6 +5716,8 @@ "integrity": "sha512-VMC7Frt87Oo0AOJ6EcPFbi+tZmkQ4tD85aatwyWL6I9cYMJmm2e+pXUJsfGZ36U7MffXtjou2XIiWJMtHriErw==", "dev": true, "hasInstallScript": true, + "optional": true, + "peer": true, "dependencies": { "detect-libc": "^1.0.3" }, @@ -6304,6 +6258,26 @@ "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", "dev": true }, + "node_modules/html-loader": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-3.1.0.tgz", + "integrity": "sha512-ycMYFRiCF7YANcLDNP72kh3Po5pTcH+bROzdDwh00iVOAY/BwvpuZ1BKPziQ35Dk9D+UD84VGX1Lu/H4HpO4fw==", + "dev": true, + "dependencies": { + "html-minifier-terser": "^6.0.2", + "parse5": "^6.0.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, "node_modules/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -6840,15 +6814,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "node_modules/json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "dependencies": { - "jsonify": "~0.0.0" - } - }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -6873,15 +6838,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/jsprim": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", @@ -7679,6 +7635,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -8295,9 +8257,9 @@ } }, "node_modules/postcss-merge-longhand": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.4.tgz", - "integrity": "sha512-hbqRRqYfmXoGpzYKeW0/NCZhvNyQIlQeWVSao5iKWdyx7skLvCfQFGIUsP9NUs3dSbPac2IC4Go85/zG+7MlmA==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.5.tgz", + "integrity": "sha512-NOG1grw9wIO+60arKa2YYsrbgvP6tp+jqc7+ZD5/MalIw234ooH2C6KlR6FEn4yle7GqZoBxSK1mLBE9KPur6w==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0", @@ -9523,9 +9485,9 @@ "dev": true }, "node_modules/sass": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.52.0.tgz", - "integrity": "sha512-6EnTglag2oVD8sNJCxUi2Jd3ICH9tJ5Mqudt/gIZNBR2uKJUBZuTpW9O1t04SkDLP7VFa76FCWTV2rwchqM8Kw==", + "version": "1.52.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.52.1.tgz", + "integrity": "sha512-fSzYTbr7z8oQnVJ3Acp9hV80dM1fkMN7mSD/25mpcct9F7FPBMOI8krEYALgU1aZoqGhQNhTPsuSmxjnIvAm4Q==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -10629,69 +10591,6 @@ "node": ">=8" } }, - "node_modules/tsickle": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.34.3.tgz", - "integrity": "sha512-mb1v3nsr6rYaZky22xj0d6qv4ogAR40Bc6r37jwWOg3bEIO/ZppEFZiEADs/NNVLcWTPgmNmPZgaX5CfAH6oXA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map": "^0.7.3" - }, - "bin": { - "tsickle": "src/main.js" - }, - "peerDependencies": { - "typescript": "~3.3.1" - } - }, - "node_modules/tsickle-loader": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/tsickle-loader/-/tsickle-loader-0.5.0.tgz", - "integrity": "sha512-VzJCouScljE7JB40bQo9fOu0Rrw8pCIfj8tkTA8kdvQWbDEskdPrPyEnHTpRHyrnIo0zw+TrJnxnrSL+fH//nQ==", - "dev": true, - "dependencies": { - "fs-extra": "^7.0.1", - "tsickle": "^0.34.3" - }, - "peerDependencies": { - "typescript": "~3.3.1", - "webpack": "^4.29.1" - } - }, - "node_modules/tsickle-loader/node_modules/fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/tsickle-loader/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/tsickle/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", @@ -10765,15 +10664,6 @@ "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", "dev": true }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -11564,14 +11454,21 @@ "dev": true }, "node_modules/ws": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", - "integrity": "sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-2.3.1.tgz", + "integrity": "sha1-a5Sz5EfLajY/eF6vlK9jWejoHIA=", "dev": true, "dependencies": { - "async-limiter": "~1.0.0" + "safe-buffer": "~5.0.1", + "ultron": "~1.1.0" } }, + "node_modules/ws/node_modules/safe-buffer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", + "dev": true + }, "node_modules/xml-js": { "version": "1.6.11", "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", @@ -11752,13 +11649,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.1.tgz", "integrity": "sha512-f1G1WGDXEU/RN1TWAxBPQgQudtLnLQPyiWdtypkPC+mVYNKFKH/HYXSxH4MVNqwF8M0eDsoiU7HumJHCg/L/jg==", - "dev": true + "dev": true, + "requires": {} }, "@csstools/selector-specificity": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-1.0.0.tgz", "integrity": "sha512-RkYG5KiGNX0fJ5YoI0f4Wfq2Yo74D25Hru4fxTOioYdQvHBxcrrtTTyT5Ozzh2ejcNrhFy7IEts2WyEY7yi5yw==", - "dev": true + "dev": true, + "requires": {} }, "@discoveryjs/json-ext": { "version": "0.5.7", @@ -14348,7 +14247,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz", "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==", - "dev": true + "dev": true, + "requires": {} }, "@webpack-cli/info": { "version": "1.4.1", @@ -14363,7 +14263,8 @@ "version": "1.6.1", "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz", "integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==", - "dev": true + "dev": true, + "requires": {} }, "@xtuc/ieee754": { "version": "1.2.0", @@ -14397,7 +14298,8 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true + "dev": true, + "requires": {} }, "adjust-sourcemap-loader": { "version": "4.0.0", @@ -15536,27 +15438,12 @@ "dev": true }, "chromecast-device-emulator": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/chromecast-device-emulator/-/chromecast-device-emulator-1.2.7.tgz", - "integrity": "sha512-A93dyExP1j/Ho/n5Sqrm3uqvzFBsI5jmFrJRnqXt6nHAKDvK/mg+722R3V+OaGieWXiZmgVpi4snF6PrnNpVhw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/chromecast-device-emulator/-/chromecast-device-emulator-1.1.2.tgz", + "integrity": "sha512-VNaYLHsUatBtXy7uhueaQfY8yVJ87l/CUrkuGbDSrLNtAPa3DZSeurw5csfjx9Z+bgfd5WFmHM4yfcAVriM4+w==", "dev": true, "requires": { - "ajv": "^4.11.8", - "chalk": "^2.4.1", - "commander": "^2.15.1", - "ws": "^5.2.0" - }, - "dependencies": { - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha512-I/bSHSNEcFFqXLf91nchoNB9D1Kie3QKcWdchYUaoIg1+1bdWDkdfdlvdIOJbi9U8xR0y+MWc5D+won9v95WlQ==", - "dev": true, - "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" - } - } + "ws": "^2.3.1" } }, "clean-css": { @@ -15587,12 +15474,6 @@ "shallow-clone": "^3.0.0" } }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -15798,7 +15679,8 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz", "integrity": "sha512-Ufadglr88ZLsrvS11gjeu/40Lw74D9Am/Jpr3LlYm5Q4ZP5KdlUhG+6u2EjyXeZcxmZ2h1ebCKngDjolpeLHpg==", - "dev": true + "dev": true, + "requires": {} }, "css-has-pseudo": { "version": "3.0.4", @@ -15840,7 +15722,8 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "dev": true + "dev": true, + "requires": {} }, "css-select": { "version": "4.3.0", @@ -15892,20 +15775,20 @@ "dev": true }, "cssnano": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.8.tgz", - "integrity": "sha512-5lma/yQlK+6eOHSUqNAS11b4/fbiuasoxmCHoVYxSg6lQsyX7bGGIqiLi4o3Pe2CrUTrgcD2udW7JIgzC2806g==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.9.tgz", + "integrity": "sha512-hctQHIIeDrfMjq0bQhoVmRVaSeNNOGxkvkKVOcKpJzLr09wlRrZWH4GaYudp0aszpW8wJeaO5/yBmID9n7DNCg==", "dev": true, "requires": { - "cssnano-preset-default": "^5.2.8", + "cssnano-preset-default": "^5.2.9", "lilconfig": "^2.0.3", "yaml": "^1.10.2" } }, "cssnano-preset-default": { - "version": "5.2.8", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.8.tgz", - "integrity": "sha512-6xQXUhTAPupvib3KC0Gl0d1jIwGFcJyuWQiMcA6grprGdmIzt1cxG5z78VuZu6DRRS6qin6ETkQsH6ixxb/SQw==", + "version": "5.2.9", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.9.tgz", + "integrity": "sha512-/4qcQcAfFEg+gnXE5NxKmYJ9JcT+8S5SDuJCLYMDN8sM/ymZ+lgLXq5+ohx/7V2brUCkgW2OaoCzOdAN0zvhGw==", "dev": true, "requires": { "css-declaration-sorter": "^6.2.2", @@ -15917,7 +15800,7 @@ "postcss-discard-duplicates": "^5.1.0", "postcss-discard-empty": "^5.1.1", "postcss-discard-overridden": "^5.1.0", - "postcss-merge-longhand": "^5.1.4", + "postcss-merge-longhand": "^5.1.5", "postcss-merge-rules": "^5.1.1", "postcss-minify-font-values": "^5.1.0", "postcss-minify-gradients": "^5.1.1", @@ -15943,7 +15826,8 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", - "dev": true + "dev": true, + "requires": {} }, "csso": { "version": "4.2.0", @@ -16018,7 +15902,9 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "detect-node": { "version": "2.1.0", @@ -16229,21 +16115,6 @@ "binwrap": "^0.2.3" } }, - "elm-hot": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/elm-hot/-/elm-hot-1.1.6.tgz", - "integrity": "sha512-zYZJlfs7Gt4BdjA+D+857K+XAWzwwySJmXCgFpHW1dIEfaHSZCIPYPf7/jinZBLfKRkOAlKzI32AA84DY50g7Q==", - "dev": true - }, - "elm-hot-webpack-loader": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/elm-hot-webpack-loader/-/elm-hot-webpack-loader-1.1.8.tgz", - "integrity": "sha512-QXggtyhzeg7ioV8Ujoc6elFY+QYr49hFEb5EDcZP7976w62vjfqFX8YTa73alA+ayLT00ZsIU6eFjAzvLUzF0g==", - "dev": true, - "requires": { - "elm-hot": "^1.1.6" - } - }, "elm-webpack-loader": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/elm-webpack-loader/-/elm-webpack-loader-8.0.0.tgz", @@ -16669,6 +16540,8 @@ "resolved": "https://registry.npmjs.org/fibers/-/fibers-5.0.1.tgz", "integrity": "sha512-VMC7Frt87Oo0AOJ6EcPFbi+tZmkQ4tD85aatwyWL6I9cYMJmm2e+pXUJsfGZ36U7MffXtjou2XIiWJMtHriErw==", "dev": true, + "optional": true, + "peer": true, "requires": { "detect-libc": "^1.0.3" } @@ -16699,7 +16572,8 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "json-schema-traverse": { "version": "0.4.1", @@ -17062,6 +16936,16 @@ "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", "dev": true }, + "html-loader": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-3.1.0.tgz", + "integrity": "sha512-ycMYFRiCF7YANcLDNP72kh3Po5pTcH+bROzdDwh00iVOAY/BwvpuZ1BKPziQ35Dk9D+UD84VGX1Lu/H4HpO4fw==", + "dev": true, + "requires": { + "html-minifier-terser": "^6.0.2", + "parse5": "^6.0.1" + } + }, "html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -17211,7 +17095,8 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true + "dev": true, + "requires": {} }, "immutable": { "version": "4.0.0", @@ -17454,15 +17339,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "requires": { - "jsonify": "~0.0.0" - } - }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -17484,12 +17360,6 @@ "graceful-fs": "^4.1.6" } }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, "jsprim": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", @@ -18107,6 +17977,12 @@ "lines-and-columns": "^1.1.6" } }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -18314,7 +18190,8 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz", "integrity": "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==", - "dev": true + "dev": true, + "requires": {} }, "postcss-custom-properties": { "version": "12.1.7", @@ -18347,25 +18224,29 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.1.tgz", "integrity": "sha512-5JscyFmvkUxz/5/+TB3QTTT9Gi9jHkcn8dcmmuN68JQcv3aQg4y88yEHHhwFB52l/NkaJ43O0dbksGMAo49nfQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-discard-duplicates": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-discard-empty": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", - "dev": true + "dev": true, + "requires": {} }, "postcss-discard-overridden": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-double-position-gradients": { "version": "3.1.1", @@ -18408,13 +18289,15 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "dev": true + "dev": true, + "requires": {} }, "postcss-gap-properties": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz", "integrity": "sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-image-set-function": { "version": "4.0.6", @@ -18440,7 +18323,8 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-lab-function": { "version": "4.2.0", @@ -18467,18 +18351,20 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "dev": true + "dev": true, + "requires": {} }, "postcss-media-minmax": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-merge-longhand": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.4.tgz", - "integrity": "sha512-hbqRRqYfmXoGpzYKeW0/NCZhvNyQIlQeWVSao5iKWdyx7skLvCfQFGIUsP9NUs3dSbPac2IC4Go85/zG+7MlmA==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.5.tgz", + "integrity": "sha512-NOG1grw9wIO+60arKa2YYsrbgvP6tp+jqc7+ZD5/MalIw234ooH2C6KlR6FEn4yle7GqZoBxSK1mLBE9KPur6w==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0", @@ -18571,7 +18457,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -18616,7 +18503,8 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", - "dev": true + "dev": true, + "requires": {} }, "postcss-normalize-display-values": { "version": "5.1.0", @@ -18735,13 +18623,15 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz", "integrity": "sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==", - "dev": true + "dev": true, + "requires": {} }, "postcss-page-break": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-place": { "version": "7.0.4", @@ -18868,7 +18758,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-selector-not": { "version": "5.0.0", @@ -19313,9 +19204,9 @@ "dev": true }, "sass": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.52.0.tgz", - "integrity": "sha512-6EnTglag2oVD8sNJCxUi2Jd3ICH9tJ5Mqudt/gIZNBR2uKJUBZuTpW9O1t04SkDLP7VFa76FCWTV2rwchqM8Kw==", + "version": "1.52.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.52.1.tgz", + "integrity": "sha512-fSzYTbr7z8oQnVJ3Acp9hV80dM1fkMN7mSD/25mpcct9F7FPBMOI8krEYALgU1aZoqGhQNhTPsuSmxjnIvAm4Q==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", @@ -19718,7 +19609,8 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", - "dev": true + "dev": true, + "requires": {} }, "stylehacks": { "version": "5.1.0", @@ -19940,7 +19832,8 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "json-schema-traverse": { "version": "0.4.1", @@ -20113,57 +20006,6 @@ } } }, - "tsickle": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.34.3.tgz", - "integrity": "sha512-mb1v3nsr6rYaZky22xj0d6qv4ogAR40Bc6r37jwWOg3bEIO/ZppEFZiEADs/NNVLcWTPgmNmPZgaX5CfAH6oXA==", - "dev": true, - "requires": { - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, - "tsickle-loader": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/tsickle-loader/-/tsickle-loader-0.5.0.tgz", - "integrity": "sha512-VzJCouScljE7JB40bQo9fOu0Rrw8pCIfj8tkTA8kdvQWbDEskdPrPyEnHTpRHyrnIo0zw+TrJnxnrSL+fH//nQ==", - "dev": true, - "requires": { - "fs-extra": "^7.0.1", - "tsickle": "^0.34.3" - }, - "dependencies": { - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - } - } - }, "tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", @@ -20218,12 +20060,6 @@ "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", "dev": true }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -20371,7 +20207,8 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "browserslist": { "version": "4.20.3", @@ -20726,7 +20563,8 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz", "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==", - "dev": true + "dev": true, + "requires": {} } } }, @@ -20796,12 +20634,21 @@ "dev": true }, "ws": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", - "integrity": "sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-2.3.1.tgz", + "integrity": "sha1-a5Sz5EfLajY/eF6vlK9jWejoHIA=", "dev": true, "requires": { - "async-limiter": "~1.0.0" + "safe-buffer": "~5.0.1", + "ultron": "~1.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", + "dev": true + } } }, "xml-js": { diff --git a/client/package.json b/client/package.json index 0149211e..5ab5c100 100644 --- a/client/package.json +++ b/client/package.json @@ -47,18 +47,17 @@ "@types/chromecast-caf-sender": "^1.0.3", "ajv": "^8.8.2", "app-manifest-loader": "^2.4.1", - "chromecast-device-emulator": "^1.2.7", + "chromecast-device-emulator": "^1.1.2", "compression-webpack-plugin": "^10.0.0", "css-hot-loader": "^1.4.4", "css-loader": "^6.3.0", "cssnano": "^5.0.5", "elm": "^0.19.1-3", "elm-analyse": "^0.16.5", - "elm-hot-webpack-loader": "^1.1.7", "elm-webpack-loader": "^8.0.0", "extract-loader": "^5.1.0", - "fibers": "^5.0.0", "file-loader": "^6.2.0", + "html-loader": "^3.1.0", "html-webpack-plugin": "^5.3.1", "mini-css-extract-plugin": "^2.3.0", "postcss-import": "^14.0.0", @@ -72,7 +71,6 @@ "style-loader": "^3.3.0", "terser-webpack-plugin": "^5.0.3", "ts-loader": "^9.2.6", - "tsickle-loader": "^0.5.0", "typescript": "^4.1.3", "webpack": "^5.56.1", "webpack-cli": "^4.3.1", diff --git a/client/src/html/index.html b/client/src/html/index.html index 18918ebe..16c82974 100644 --- a/client/src/html/index.html +++ b/client/src/html/index.html @@ -1,20 +1,33 @@ - + - - + + - Massive Decks - + Massive Decks + - - + + - - - - - - + + + + + + + + diff --git a/client/webpack.config.js b/client/webpack.config.js index c2556501..385031a3 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -20,6 +20,7 @@ module.exports = (env, argv) => { const dist = path.resolve(__dirname, "dist"); const src = path.resolve(__dirname, "src"); + const assets = path.resolve(__dirname, "assets"); const version = process.env["MD_VERSION"]; const metadataFilename = `${src}/elm/MassiveDecks/Version.elm`; @@ -76,7 +77,6 @@ module.exports = (env, argv) => { test: /\.elm$/, exclude: [/elm-stuff/, /node_modules/], use: [ - ...(production ? [] : [{ loader: "elm-hot-webpack-loader" }]), { loader: "elm-webpack-loader", options: { diff --git a/deployment/postgres/docker_compose.yml b/deployment/postgres/docker_compose.yml index 4a509639..96e5ac38 100644 --- a/deployment/postgres/docker_compose.yml +++ b/deployment/postgres/docker_compose.yml @@ -14,7 +14,7 @@ volumes: services: storage: - image: "postgres:13" + image: "postgres:14" # See the postgres image's documentation for more on configuring it. # https://hub.docker.com/_/postgres environment: diff --git a/server/.eslintrc.json b/server/.eslintrc.json index 98069310..df0a4564 100644 --- a/server/.eslintrc.json +++ b/server/.eslintrc.json @@ -1,39 +1,18 @@ { + "root": true, "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint", "simple-import-sort"], "extends": [ + "eslint:recommended", "plugin:@typescript-eslint/recommended", - "prettier/@typescript-eslint", "plugin:prettier/recommended" ], - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module" - }, "rules": { - "no-template-curly-in-string": "warn", - "guard-for-in": "error", - "no-eval": "error", - "arrow-body-style": "error", - - "no-unused-vars": "off", + "simple-import-sort/imports": "error", "@typescript-eslint/no-unused-vars": [ "error", - { - "args": "none", - "argsIgnorePattern": "^_$", - "varsIgnorePattern": "^_$" - } + { "argsIgnorePattern": "^_", "destructuredArrayIgnorePattern": "^_" } ], - - "@typescript-eslint/explicit-function-return-type": [ - "error", - { - "allowExpressions": true, - "allowTypedFunctionExpressions": true, - "allowHigherOrderFunctions": true - } - ], - "@typescript-eslint/no-empty-interface": "off" } } diff --git a/server/Dockerfile b/server/Dockerfile index e4f3776e..178baed2 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,4 +1,4 @@ -ARG NODE_VERSION=17 +ARG NODE_VERSION=18 ARG MODE=production ARG BUILD_DATE ARG VCS_REF @@ -47,4 +47,4 @@ COPY --from=build ["/md/dist", "./"] EXPOSE 8081 USER node -CMD ["node", "--es-module-specifier-resolution=node", "./index.js"] +CMD ["node", "./index.js"] diff --git a/server/Dockerfile.dev b/server/Dockerfile.dev index cfbe4f47..b58d617a 100644 --- a/server/Dockerfile.dev +++ b/server/Dockerfile.dev @@ -1,4 +1,4 @@ -ARG NODE_VERSION=17 +ARG NODE_VERSION=18 FROM node:${NODE_VERSION}-alpine diff --git a/server/package-lock.json b/server/package-lock.json index adf939a7..ae7fb83f 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -48,12 +48,13 @@ "eslint": "^8.3.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-simple-import-sort": "^7.0.0", "nodemon": "^2.0.6", "prettier": "2.6.2", - "typescript": "^4.1.3" + "typescript": "^4.8.0-dev.20220520" }, "engines": { - "node": "18.x.x" + "node": ">=18.0.0 <19.0.0" } }, "node_modules/@colors/colors": { @@ -75,15 +76,15 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.3.tgz", - "integrity": "sha512-uGo44hIwoLGNyduRpjdEpovcbMdd+Nv7amtmJxnKmI8xj6yd5LncmSwDa5NgX/41lIFJtkjD6YdVfgEzPfJ5UA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.3.2", - "globals": "^13.9.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -377,6 +378,35 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/@typescript-eslint/parser": { "version": "5.25.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.25.0.tgz", @@ -447,6 +477,35 @@ } } }, + "node_modules/@typescript-eslint/type-utils/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/@typescript-eslint/types": { "version": "5.25.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.25.0.tgz", @@ -487,6 +546,35 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/@typescript-eslint/utils": { "version": "5.25.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.25.0.tgz", @@ -1292,12 +1380,12 @@ } }, "node_modules/eslint": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.15.0.tgz", - "integrity": "sha512-GG5USZ1jhCu8HJkzGgeK8/+RGnHaNYZGrGDzUtigK3BsGESW/rs2az23XqE0WVwDxy1VRvvjSSGu5nB0Bu+6SA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", + "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.2.3", + "@eslint/eslintrc": "^1.3.0", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -1315,7 +1403,7 @@ "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", - "globals": "^13.6.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -1376,6 +1464,15 @@ } } }, + "node_modules/eslint-plugin-simple-import-sort": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-7.0.0.tgz", + "integrity": "sha512-U3vEDB5zhYPNfxT5TYR7u01dboFZp+HNpnGhkDB2g/2E4wZ/g1Q9Ton8UwCLfRV9yAKyYqDh62oHOamvkFxsvw==", + "dev": true, + "peerDependencies": { + "eslint": ">=5.0.0" + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -3778,21 +3875,6 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3839,9 +3921,9 @@ } }, "node_modules/typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "version": "4.8.0-dev.20220520", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.0-dev.20220520.tgz", + "integrity": "sha512-OLLEy7nRAUVunVn893E0QLf+pNitQQa+CBN4ZR3LpTIbK2VSPTIPX5wAFfVPZeuxXWn8mzCOammnyTAMFWpoEg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -4130,15 +4212,15 @@ } }, "@eslint/eslintrc": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.3.tgz", - "integrity": "sha512-uGo44hIwoLGNyduRpjdEpovcbMdd+Nv7amtmJxnKmI8xj6yd5LncmSwDa5NgX/41lIFJtkjD6YdVfgEzPfJ5UA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.3.2", - "globals": "^13.9.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -4391,6 +4473,24 @@ "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" + }, + "dependencies": { + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "dev": true, + "peer": true + } } }, "@typescript-eslint/parser": { @@ -4424,6 +4524,24 @@ "@typescript-eslint/utils": "5.25.0", "debug": "^4.3.4", "tsutils": "^3.21.0" + }, + "dependencies": { + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "dev": true, + "peer": true + } } }, "@typescript-eslint/types": { @@ -4445,6 +4563,24 @@ "is-glob": "^4.0.3", "semver": "^7.3.7", "tsutils": "^3.21.0" + }, + "dependencies": { + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "dev": true, + "peer": true + } } }, "@typescript-eslint/utils": { @@ -5064,12 +5200,12 @@ "dev": true }, "eslint": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.15.0.tgz", - "integrity": "sha512-GG5USZ1jhCu8HJkzGgeK8/+RGnHaNYZGrGDzUtigK3BsGESW/rs2az23XqE0WVwDxy1VRvvjSSGu5nB0Bu+6SA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", + "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.2.3", + "@eslint/eslintrc": "^1.3.0", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -5087,7 +5223,7 @@ "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", - "globals": "^13.6.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -5158,6 +5294,13 @@ "prettier-linter-helpers": "^1.0.0" } }, + "eslint-plugin-simple-import-sort": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-7.0.0.tgz", + "integrity": "sha512-U3vEDB5zhYPNfxT5TYR7u01dboFZp+HNpnGhkDB2g/2E4wZ/g1Q9Ton8UwCLfRV9yAKyYqDh62oHOamvkFxsvw==", + "dev": true, + "requires": {} + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -6930,15 +7073,6 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6973,9 +7107,9 @@ } }, "typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "version": "4.8.0-dev.20220520", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.0-dev.20220520.tgz", + "integrity": "sha512-OLLEy7nRAUVunVn893E0QLf+pNitQQa+CBN4ZR3LpTIbK2VSPTIPX5wAFfVPZeuxXWn8mzCOammnyTAMFWpoEg==", "dev": true }, "undefsafe": { diff --git a/server/package.json b/server/package.json index af8b38d0..b3283243 100644 --- a/server/package.json +++ b/server/package.json @@ -7,19 +7,23 @@ "private": true, "repository": "github:Lattyware/massivedecks", "engines": { - "node": "18.x.x" + "node": ">=18.0.0 <19.0.0" }, "type": "module", - "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js" + } + }, "scripts": { "build": "npx tsc", "prestart": "npm run build", - "start": "node --es-module-specifier-resolution=node dist/index.js", + "start": "node dist/index.js", "preinspect": "npm run build", - "inspect": "node --inspect-brk --es-module-specifier-resolution=node dist/index.js", + "inspect": "node --inspect-brk dist/index.js", "dev": "nodemon --exec \"npm run start\"", "debug": "nodemon --exec \"npm run inspect\"", - "generate-secret": "npm run prestart && node --es-module-specifier-resolution=node dist/secret.js", + "generate-secret": "npm run prestart && node dist/secret.js", "docker:build": "mkdir -p \"$(pwd)/../workaround\" && ROOT_DIR=\"$(pwd)/..\" USER_ID=$(id -u ${USER}) GROUP_ID=$(id -g ${USER}) docker compose -f ../develop.yml run --rm server run build", "docker:generate-secret": "mkdir -p \"$(pwd)/../workaround\" && ROOT_DIR=\"$(pwd)/..\" USER_ID=$(id -u ${USER}) GROUP_ID=$(id -g ${USER}) docker compose -f ../develop.yml run --rm server run generate-secret", "docker:dev": "mkdir -p \"$(pwd)/../workaround\" && ROOT_DIR=\"$(pwd)/..\" USER_ID=$(id -u ${USER}) GROUP_ID=$(id -g ${USER}) docker compose -f ../develop.yml up --build --no-log-prefix server" @@ -68,9 +72,10 @@ "eslint": "^8.3.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-simple-import-sort": "^7.0.0", "nodemon": "^2.0.6", "prettier": "2.6.2", - "typescript": "^4.1.3" + "typescript": "^4.8.0-dev.20220520" }, "nodemonConfig": { "ext": "ts,js,json,json5", diff --git a/server/src/ts/action.ts b/server/src/ts/action.ts index e669d512..8718efed 100644 --- a/server/src/ts/action.ts +++ b/server/src/ts/action.ts @@ -1,13 +1,13 @@ -import * as Actions from "./action/actions"; -import * as Authenticate from "./action/authenticate"; -import * as GameAction from "./action/game-action"; -import * as Handler from "./action/handler"; -import * as Leave from "./action/leave"; -import * as Privileged from "./action/privileged"; -import * as SetUserRole from "./action/set-user-role"; -import * as Validation from "./action/validation.validator"; -import { AlreadyAuthenticatedError } from "./errors/authentication"; -import { InvalidActionError } from "./errors/validation"; +import * as Actions from "./action/actions.js"; +import * as Authenticate from "./action/authenticate.js"; +import * as GameAction from "./action/game-action.js"; +import type * as Handler from "./action/handler.js"; +import * as Leave from "./action/leave.js"; +import * as Privileged from "./action/privileged.js"; +import * as SetUserRole from "./action/set-user-role.js"; +import * as Validation from "./action/validation.validator.js"; +import { AlreadyAuthenticatedError } from "./errors/authentication.js"; +import { InvalidActionError } from "./errors/validation.js"; /** * An action a user takes to affect the game in some way, received via a @@ -24,7 +24,7 @@ const allActions = new Actions.PassThroughGroup( GameAction.actions, Privileged.actions, SetUserRole.actions, - Leave.actions + Leave.actions, ); const _validateAction = Validation.validate("Action"); @@ -41,7 +41,7 @@ export const handle: Handler.Handler = ( auth, lobby, action, - config + config, ) => { const validated = validate(action); if (Authenticate.is(validated)) { diff --git a/server/src/ts/action/actions.ts b/server/src/ts/action/actions.ts index 5415d596..97140716 100644 --- a/server/src/ts/action/actions.ts +++ b/server/src/ts/action/actions.ts @@ -1,11 +1,12 @@ import wu from "wu"; -import { Action } from "../action"; -import { InvalidActionError } from "../errors/validation"; -import { Lobby } from "../lobby"; -import { Change } from "../lobby/change"; -import { ServerState } from "../server-state"; -import * as Token from "../user/token"; -import * as Handler from "./handler"; + +import type { Action } from "../action.js"; +import { InvalidActionError } from "../errors/validation.js"; +import type { Lobby } from "../lobby.js"; +import type { Change } from "../lobby/change.js"; +import type { ServerState } from "../server-state.js"; +import type * as Token from "../user/token.js"; +import type * as Handler from "./handler.js"; export interface Actions { is: (action: Action) => boolean; @@ -14,7 +15,7 @@ export interface Actions { auth: Token.Claims, lobby: ParentLobby, action: Parent, - server: ServerState + server: ServerState, ) => Change | undefined; } @@ -22,8 +23,9 @@ export abstract class Implementation< ParentType extends Action, Type extends ParentType & { action: Name }, Name extends string, - ParentLobby extends Lobby -> implements Actions { + ParentLobby extends Lobby, +> implements Actions +{ protected abstract readonly name: Name; // Should be Action, broken due to https://github.com/microsoft/TypeScript/pull/37195 @@ -41,7 +43,7 @@ export abstract class Implementation< auth: Token.Claims, lobby: ParentLobby, action: Action, - server: ServerState + server: ServerState, ): Change | undefined { if (this.is(action)) { return this.handle(auth, lobby, action, server); @@ -57,8 +59,9 @@ export abstract class Group< ParentType extends Action, Type extends ParentType, ParentLobby extends Lobby, - LimitedLobby extends ParentLobby -> implements Actions { + LimitedLobby extends ParentLobby, +> implements Actions +{ private readonly childActions: Actions[]; protected constructor(...childActions: Actions[]) { @@ -73,14 +76,14 @@ export abstract class Group< auth: Token.Claims, lobby: ParentLobby, action: Type, - server: ServerState + server: ServerState, ): lobby is LimitedLobby; tryHandle( auth: Token.Claims, lobby: ParentLobby, action: Action, - server: ServerState + server: ServerState, ): Change | undefined { if (this.is(action)) { if (this.limit(auth, lobby, action, server)) { @@ -104,17 +107,17 @@ export abstract class Group< */ export class PassThroughGroup< Type extends Action, - ParentLobby extends Lobby + ParentLobby extends Lobby, > extends Group { constructor(...childActions: Actions[]) { super(...childActions); } public limit( - auth: Token.Claims, + _auth: Token.Claims, lobby: ParentLobby, - action: Type, - server: ServerState + _action: Type, + _server: ServerState, ): lobby is ParentLobby { return true; } diff --git a/server/src/ts/action/authenticate.ts b/server/src/ts/action/authenticate.ts index fa23fc2a..edb8e3c9 100644 --- a/server/src/ts/action/authenticate.ts +++ b/server/src/ts/action/authenticate.ts @@ -1,11 +1,11 @@ -import { Action } from "../action"; -import * as Authentication from "../errors/authentication"; -import * as Event from "../event"; -import * as ConnectionChanged from "../events/lobby-event/connection-changed"; -import * as Change from "../lobby/change"; -import { GameCode } from "../lobby/game-code"; -import { ServerState } from "../server-state"; -import * as Token from "../user/token"; +import type { Action } from "../action.js"; +import * as Authentication from "../errors/authentication.js"; +import * as Event from "../event.js"; +import * as ConnectionChanged from "../events/lobby-event/connection-changed.js"; +import * as Change from "../lobby/change.js"; +import type { GameCode } from "../lobby/game-code.js"; +import type { ServerState } from "../server-state.js"; +import * as Token from "../user/token.js"; /** * Authenticate with the game. @@ -34,12 +34,12 @@ export const is = (action: Action): action is Authenticate => export async function handle( server: ServerState, authenticate: Authenticate, - gameCode: GameCode + gameCode: GameCode, ): Promise { const claims = Token.validate( authenticate.token, await server.store.id(), - server.config.secret + server.config.secret, ); if (claims.gc !== gameCode) { throw new Authentication.InvalidAuthenticationError("wrong game code"); diff --git a/server/src/ts/action/game-action.ts b/server/src/ts/action/game-action.ts index 27fae20e..bcbc5bbe 100644 --- a/server/src/ts/action/game-action.ts +++ b/server/src/ts/action/game-action.ts @@ -1,18 +1,18 @@ -import * as Action from "../action"; -import * as ActionExecutionError from "../errors/action-execution-error"; -import { GameNotStartedError } from "../errors/action-execution-error"; -import { Game } from "../games/game"; -import * as Player from "../games/player"; -import * as Lobby from "../lobby"; -import { ServerState } from "../server-state"; -import * as Token from "../user/token"; -import * as Actions from "./actions"; -import * as Czar from "./game-action/czar"; -import * as EnforceTimeLimit from "./game-action/enforce-time-limit"; -import * as PlayerAction from "./game-action/player"; -import * as Redraw from "./game-action/redraw"; -import * as SetPresence from "./game-action/set-presence"; -import * as Like from "./game-action/like"; +import type * as Action from "../action.js"; +import * as ActionExecutionError from "../errors/action-execution-error.js"; +import { GameNotStartedError } from "../errors/action-execution-error.js"; +import type { Game } from "../games/game.js"; +import * as Player from "../games/player.js"; +import * as Lobby from "../lobby.js"; +import type { ServerState } from "../server-state.js"; +import type * as Token from "../user/token.js"; +import * as Actions from "./actions.js"; +import * as Czar from "./game-action/czar.js"; +import * as EnforceTimeLimit from "./game-action/enforce-time-limit.js"; +import * as Like from "./game-action/like.js"; +import * as PlayerAction from "./game-action/player.js"; +import * as Redraw from "./game-action/redraw.js"; +import * as SetPresence from "./game-action/set-presence.js"; /** * An action only a player can perform. @@ -38,15 +38,15 @@ class GameActions extends Actions.Group< Redraw.actions, EnforceTimeLimit.actions, SetPresence.actions, - Like.actions + Like.actions, ); } public limit( - auth: Token.Claims, + _auth: Token.Claims, lobby: Lobby.Lobby, action: GameAction, - server: ServerState + _server: ServerState, ): lobby is Lobby.WithActiveGame { if (!Lobby.hasActiveGame(lobby)) { throw new GameNotStartedError(action); @@ -61,13 +61,13 @@ export function expectRole( auth: Token.Claims, action: Czar.Czar, game: Game, - expected: "Czar" + expected: "Czar", ): void; export function expectRole( auth: Token.Claims, action: PlayerAction.Player, game: Game, - expected: "Player" + expected: "Player", ): void; /** * Expect a given role. @@ -80,14 +80,14 @@ export function expectRole( auth: Token.Claims, action: GameAction, game: Game, - expected: Player.Role + expected: Player.Role, ): void { const playerRole = Player.role(auth.uid, game); if (playerRole !== expected) { throw new ActionExecutionError.IncorrectPlayerRoleError( action, playerRole, - expected + expected, ); } } diff --git a/server/src/ts/action/game-action/czar.ts b/server/src/ts/action/game-action/czar.ts index b85b8ef0..e9b5e803 100644 --- a/server/src/ts/action/game-action/czar.ts +++ b/server/src/ts/action/game-action/czar.ts @@ -1,11 +1,11 @@ -import * as Action from "../../action"; -import * as Lobby from "../../lobby"; -import * as Token from "../../user/token"; -import * as GameAction from "../game-action"; -import * as Actions from "../actions"; -import * as Judge from "./czar/judge"; -import * as PickCall from "./czar/pick-call"; -import * as Reveal from "./czar/reveal"; +import type * as Action from "../../action.js"; +import type * as Lobby from "../../lobby.js"; +import type * as Token from "../../user/token.js"; +import * as Actions from "../actions.js"; +import * as GameAction from "../game-action.js"; +import * as Judge from "./czar/judge.js"; +import * as PickCall from "./czar/pick-call.js"; +import * as Reveal from "./czar/reveal.js"; /** * An action only the czar can perform. @@ -25,7 +25,7 @@ class CzarActions extends Actions.Group< limit( auth: Token.Claims, lobby: Lobby.WithActiveGame, - action: Czar + action: Czar, ): lobby is Lobby.WithActiveGame { GameAction.expectRole(auth, action, lobby.game, "Czar"); return true; diff --git a/server/src/ts/action/game-action/czar/judge.ts b/server/src/ts/action/game-action/czar/judge.ts index 79b407a0..edaea483 100644 --- a/server/src/ts/action/game-action/czar/judge.ts +++ b/server/src/ts/action/game-action/czar/judge.ts @@ -1,13 +1,14 @@ -import { InvalidActionError } from "../../../errors/validation"; -import * as Event from "../../../event"; -import * as RoundFinished from "../../../events/game-event/round-finished"; -import * as Play from "../../../games/cards/play"; -import * as Round from "../../../games/game/round"; -import * as Lobby from "../../../lobby"; -import * as RoundStart from "../../../timeout/round-start"; -import * as Handler from "../../handler"; -import { Czar } from "../czar"; -import * as Actions from "./../../actions"; +import { InvalidActionError } from "../../../errors/validation.js"; +import * as Event from "../../../event.js"; +import * as RoundFinished from "../../../events/game-event/round-finished.js"; +import type * as Play from "../../../games/cards/play.js"; +import type * as Round from "../../../games/game/round.js"; +import type { Player } from "../../../games/player.js"; +import type * as Lobby from "../../../lobby.js"; +import * as RoundStart from "../../../timeout/round-start.js"; +import type * as Handler from "../../handler.js"; +import type { Czar } from "../czar.js"; +import * as Actions from "./../../actions.js"; /** * A user declares the winning play for a round. @@ -37,7 +38,7 @@ class JudgeAction extends Actions.Implementation< let winningPlay = undefined; for (const play of plays) { if (play.likes.length > 0) { - const player = game.players[play.playedBy]; + const player = game.players[play.playedBy] as Player; player.likes += play.likes.length; } if (play.id === action.winner) { @@ -47,7 +48,7 @@ class JudgeAction extends Actions.Implementation< if (winningPlay === undefined) { throw new InvalidActionError("Given play doesn't exist."); } - const player = game.players[winningPlay.playedBy]; + const player = game.players[winningPlay.playedBy] as Player; player.score += 1; if (game.rules.houseRules.winnersPick) { game.rules.houseRules.winnersPick.roundWinner = winningPlay.playedBy; diff --git a/server/src/ts/action/game-action/czar/pick-call.ts b/server/src/ts/action/game-action/czar/pick-call.ts index 1d6bdc81..554231df 100644 --- a/server/src/ts/action/game-action/czar/pick-call.ts +++ b/server/src/ts/action/game-action/czar/pick-call.ts @@ -1,9 +1,9 @@ -import * as Card from "../../../games/cards/card"; -import * as Round from "../../../games/game/round"; -import * as Lobby from "../../../lobby"; -import * as Actions from "../../actions"; -import * as Handler from "../../handler"; -import { Czar } from "../czar"; +import type * as Card from "../../../games/cards/card.js"; +import type * as Round from "../../../games/game/round.js"; +import type * as Lobby from "../../../lobby.js"; +import * as Actions from "../../actions.js"; +import type * as Handler from "../../handler.js"; +import type { Czar } from "../czar.js"; /** * A czar picks a call for a round. @@ -26,7 +26,7 @@ class PickCallActions extends Actions.Implementation< auth, lobby, action, - server + server, ) => { const lobbyRound = lobby.game.round; @@ -35,7 +35,7 @@ class PickCallActions extends Actions.Implementation< server, lobby.game, action.call, - action.fill + action.fill, ); lobby.game.round = round; return { lobby, events, timeouts }; diff --git a/server/src/ts/action/game-action/czar/reveal.ts b/server/src/ts/action/game-action/czar/reveal.ts index 72a7e372..94dff608 100644 --- a/server/src/ts/action/game-action/czar/reveal.ts +++ b/server/src/ts/action/game-action/czar/reveal.ts @@ -1,15 +1,15 @@ -import { InvalidActionError } from "../../../errors/validation"; -import * as Event from "../../../event"; -import * as PlayRevealed from "../../../events/game-event/play-revealed"; -import * as Play from "../../../games/cards/play"; -import * as Round from "../../../games/game/round"; -import * as Lobby from "../../../lobby"; -import * as Handler from "../../handler"; -import { Czar } from "../czar"; -import * as Actions from "./../../actions"; -import * as StoredPlay from "../../../games/game/round/stored-play"; -import * as Util from "../../../util"; -import * as FinishedRevealing from "../../../timeout/finished-revealing"; +import { InvalidActionError } from "../../../errors/validation.js"; +import * as Event from "../../../event.js"; +import * as PlayRevealed from "../../../events/game-event/play-revealed.js"; +import type * as Play from "../../../games/cards/play.js"; +import type * as Round from "../../../games/game/round.js"; +import * as StoredPlay from "../../../games/game/round/stored-play.js"; +import type * as Lobby from "../../../lobby.js"; +import * as FinishedRevealing from "../../../timeout/finished-revealing.js"; +import * as Util from "../../../util.js"; +import type * as Handler from "../../handler.js"; +import type { Czar } from "../czar.js"; +import * as Actions from "./../../actions.js"; /** * A user judges the winning play for a round. @@ -30,7 +30,7 @@ class RevealAction extends Actions.Implementation< protected handle: Handler.Custom = ( auth, lobby, - action + action, ) => { const game = lobby.game; if (game.round.verifyStage(action, "Revealing")) { @@ -45,7 +45,7 @@ class RevealAction extends Actions.Implementation< const timeouts = Util.asOptionalIterable( StoredPlay.allRevealed(game.round) ? FinishedRevealing.of(game.rules.stages) - : undefined + : undefined, ); return { lobby, diff --git a/server/src/ts/action/game-action/enforce-time-limit.ts b/server/src/ts/action/game-action/enforce-time-limit.ts index ba0537db..1b775c94 100644 --- a/server/src/ts/action/game-action/enforce-time-limit.ts +++ b/server/src/ts/action/game-action/enforce-time-limit.ts @@ -1,11 +1,11 @@ -import { InvalidActionError } from "../../errors/validation"; -import * as round from "../../games/game/round"; -import * as Lobby from "../../lobby"; -import * as change from "../../lobby/change"; -import * as Actions from "../actions"; -import { GameAction } from "../game-action"; -import * as Handler from "../handler"; -import { dealWithLostPlayer } from "./set-presence"; +import { InvalidActionError } from "../../errors/validation.js"; +import * as round from "../../games/game/round.js"; +import type * as Lobby from "../../lobby.js"; +import * as change from "../../lobby/change.js"; +import * as Actions from "../actions.js"; +import type { GameAction } from "../game-action.js"; +import type * as Handler from "../handler.js"; +import { dealWithLostPlayer } from "./set-presence.js"; /** * A player asks to enforce the soft time limit for the game. @@ -28,7 +28,7 @@ class EnforceTimeLimitAction extends Actions.Implementation< auth, lobby, action, - server + server, ) => { const game = lobby.game; const stages = game.rules.stages; @@ -51,7 +51,7 @@ class EnforceTimeLimitAction extends Actions.Implementation< } if (gameRound.timedOut) { return change.reduce(waitingFor, lobby as Lobby.WithActiveGame, (l, p) => - dealWithLostPlayer(server, l, p) + dealWithLostPlayer(server, l, p), ); } else { throw new InvalidActionError("Round stage timer not done."); diff --git a/server/src/ts/action/game-action/like.ts b/server/src/ts/action/game-action/like.ts index 94e05bb1..0c0389e4 100644 --- a/server/src/ts/action/game-action/like.ts +++ b/server/src/ts/action/game-action/like.ts @@ -1,11 +1,12 @@ -import * as Play from "../../games/cards/play"; -import * as Round from "../../games/game/round"; -import * as Lobby from "../../lobby"; -import * as Actions from "../actions"; -import * as Handler from "../handler"; -import { GameAction } from "../game-action"; -import * as PlayLiked from "../../events/game-event/play-liked"; -import * as Event from "../../event"; +import * as Event from "../../event.js"; +import * as PlayLiked from "../../events/game-event/play-liked.js"; +import type * as Play from "../../games/cards/play.js"; +import type * as Round from "../../games/game/round.js"; +import type { Player } from "../../games/player.js"; +import type * as Lobby from "../../lobby.js"; +import * as Actions from "../actions.js"; +import type { GameAction } from "../game-action.js"; +import type * as Handler from "../handler.js"; /** * A player or spectator likes a play. @@ -26,7 +27,7 @@ class LikeActions extends Actions.Implementation< protected handle: Handler.Custom = ( auth, lobby, - action + action, ) => { if ( lobby.game.round.isInStage< @@ -40,7 +41,7 @@ class LikeActions extends Actions.Implementation< target.playedBy !== auth.uid && target.likes.find((id) => id === auth.uid) === undefined ) { - lobby.game.players[target.playedBy].likes += 1; + (lobby.game.players[target.playedBy] as Player).likes += 1; target.likes.push(auth.uid); const events = lobby.game.round.stage === "Complete" diff --git a/server/src/ts/action/game-action/player.ts b/server/src/ts/action/game-action/player.ts index 930011ca..48126b95 100644 --- a/server/src/ts/action/game-action/player.ts +++ b/server/src/ts/action/game-action/player.ts @@ -1,12 +1,12 @@ -import * as Actions from "./../actions"; -import * as Submit from "./player/submit"; -import * as TakeBack from "./player/take-back"; -import * as Fill from "./player/fill"; -import * as Discard from "./player/discard"; -import * as Action from "../../action"; -import * as Lobby from "../../lobby"; -import * as Token from "../../user/token"; -import * as GameAction from "../game-action"; +import type * as Action from "../../action.js"; +import type * as Lobby from "../../lobby.js"; +import type * as Token from "../../user/token.js"; +import * as GameAction from "../game-action.js"; +import * as Actions from "./../actions.js"; +import * as Discard from "./player/discard.js"; +import * as Fill from "./player/fill.js"; +import * as Submit from "./player/submit.js"; +import * as TakeBack from "./player/take-back.js"; /** * An action only players can perform. @@ -30,7 +30,7 @@ class PlayerActions extends Actions.Group< limit( auth: Token.Claims, lobby: Lobby.WithActiveGame, - action: Player + action: Player, ): lobby is Lobby.WithActiveGame { GameAction.expectRole(auth, action, lobby.game, "Player"); return true; diff --git a/server/src/ts/action/game-action/player/discard.ts b/server/src/ts/action/game-action/player/discard.ts index 94535732..a614cc18 100644 --- a/server/src/ts/action/game-action/player/discard.ts +++ b/server/src/ts/action/game-action/player/discard.ts @@ -1,12 +1,13 @@ -import * as Card from "../../../games/cards/card"; -import * as Actions from "../../actions"; -import { Player } from "../player"; -import * as Lobby from "../../../lobby"; -import * as Handler from "../../handler"; -import { InvalidActionError } from "../../../errors/validation"; -import * as Util from "../../../util"; -import * as Event from "../../../event"; -import * as CardDiscarded from "../../../events/game-event/card-discarded"; +import { InvalidActionError } from "../../../errors/validation.js"; +import * as Event from "../../../event.js"; +import * as CardDiscarded from "../../../events/game-event/card-discarded.js"; +import type * as Card from "../../../games/cards/card.js"; +import type { Player as PlayerModel } from "../../../games/player.js"; +import type * as Lobby from "../../../lobby.js"; +import * as Util from "../../../util.js"; +import * as Actions from "../../actions.js"; +import type * as Handler from "../../handler.js"; +import type { Player } from "../player.js"; /** * Indicates the user is discarding their hand. @@ -27,15 +28,15 @@ class DiscardActions extends Actions.Implementation< protected handle: Handler.Custom = ( auth, lobby, - action + action, ) => { if (lobby.game.rules.houseRules.neverHaveIEver === undefined) { throw new InvalidActionError( - "The “Never Have I Ever” house rule is not enabled this game, but must be to do that." + "The “Never Have I Ever” house rule is not enabled this game, but must be to do that.", ); } const id = auth.uid; - const player = lobby.game.players[id]; + const player = lobby.game.players[id] as PlayerModel; const card = player.hand.find((c) => c.id === action.card); if (card === undefined) { throw new InvalidActionError("Must have the card to discard it."); @@ -45,8 +46,8 @@ class DiscardActions extends Actions.Implementation< player.hand.push(replacement); const events = Util.asOptionalIterable( Event.playerSpecificAddition(CardDiscarded.of(id, card), (to) => - id === to ? { replacement } : {} - ) + id === to ? { replacement } : {}, + ), ); return { events }; }; diff --git a/server/src/ts/action/game-action/player/fill.ts b/server/src/ts/action/game-action/player/fill.ts index ce4cc375..0b772afa 100644 --- a/server/src/ts/action/game-action/player/fill.ts +++ b/server/src/ts/action/game-action/player/fill.ts @@ -1,11 +1,12 @@ -import * as Card from "../../../games/cards/card"; -import * as Actions from "../../actions"; -import { Player } from "../player"; -import * as Lobby from "../../../lobby"; -import * as Handler from "../../handler"; -import * as Round from "../../../games/game/round"; -import { IncorrectUserRoleError } from "../../../errors/action-execution-error"; -import { InvalidActionError } from "../../../errors/validation"; +import { IncorrectUserRoleError } from "../../../errors/action-execution-error.js"; +import { InvalidActionError } from "../../../errors/validation.js"; +import * as Card from "../../../games/cards/card.js"; +import type * as Round from "../../../games/game/round.js"; +import type * as Lobby from "../../../lobby.js"; +import type { User } from "../../../user.js"; +import * as Actions from "../../actions.js"; +import type * as Handler from "../../handler.js"; +import type { Player } from "../player.js"; /** * Indicates the user has changed the value of a blank card in their hand. @@ -27,11 +28,11 @@ class FillActions extends Actions.Implementation< protected handle: Handler.Custom = ( auth, lobby, - action + action, ) => { const lobbyRound = lobby.game.round; if (lobbyRound.verifyStage(action, "Playing")) { - const user = lobby.users[auth.uid]; + const user = lobby.users[auth.uid] as User; const player = lobby.game.players[auth.uid]; if (user.role !== "Player" || player === undefined) { throw new IncorrectUserRoleError(action, user.role, "Player"); @@ -39,7 +40,7 @@ class FillActions extends Actions.Implementation< const filled = player.hand.find((c) => c.id === action.card); if (filled === undefined) { throw new InvalidActionError( - "The given card doesn't exist or isn't in the player's hand." + "The given card doesn't exist or isn't in the player's hand.", ); } if (Card.isCustom(filled)) { diff --git a/server/src/ts/action/game-action/player/submit.ts b/server/src/ts/action/game-action/player/submit.ts index 80ccf51c..b445fad7 100644 --- a/server/src/ts/action/game-action/player/submit.ts +++ b/server/src/ts/action/game-action/player/submit.ts @@ -1,16 +1,16 @@ -import { IncorrectUserRoleError } from "../../../errors/action-execution-error"; -import { InvalidActionError } from "../../../errors/validation"; -import * as Event from "../../../event"; -import * as PlaySubmitted from "../../../events/game-event/play-submitted"; -import * as Card from "../../../games/cards/card"; -import * as Play from "../../../games/cards/play"; -import * as Round from "../../../games/game/round"; -import * as Lobby from "../../../lobby"; -import * as FinishedPlaying from "../../../timeout/finished-playing"; -import * as Actions from "../../actions"; -import * as Handler from "../../handler"; -import { Player } from "../player"; -import * as Util from "../../../util"; +import { IncorrectUserRoleError } from "../../../errors/action-execution-error.js"; +import { InvalidActionError } from "../../../errors/validation.js"; +import * as Event from "../../../event.js"; +import * as PlaySubmitted from "../../../events/game-event/play-submitted.js"; +import * as Card from "../../../games/cards/card.js"; +import * as Play from "../../../games/cards/play.js"; +import type * as Round from "../../../games/game/round.js"; +import type * as Lobby from "../../../lobby.js"; +import * as FinishedPlaying from "../../../timeout/finished-playing.js"; +import * as Util from "../../../util.js"; +import * as Actions from "../../actions.js"; +import type * as Handler from "../../handler.js"; +import type { Player } from "../player.js"; /** * A player plays a response into a round. @@ -32,7 +32,7 @@ class SubmitActions extends Actions.Implementation< auth, lobby, action, - server + _server, ) => { const lobbyRound = lobby.game.round; @@ -47,7 +47,7 @@ class SubmitActions extends Actions.Implementation< if (playLength !== slotCount) { throw new InvalidActionError( "The play must have the same number of responses as the call " + - `has slots (expected ${slotCount}, got ${playLength}).` + `has slots (expected ${slotCount}, got ${playLength}).`, ); } const player = lobby.game.players[auth.uid]; @@ -59,7 +59,7 @@ class SubmitActions extends Actions.Implementation< const played = player.hand.find((c) => c.id === playedCard); if (played === undefined) { throw new InvalidActionError( - "The given card doesn't exist or isn't in the player's hand." + "The given card doesn't exist or isn't in the player's hand.", ); } extractedPlay.push(played); @@ -73,7 +73,7 @@ class SubmitActions extends Actions.Implementation< }); const events = [Event.targetAll(PlaySubmitted.of(auth.uid))]; const timeouts = Util.asOptionalIterable( - FinishedPlaying.ifNeeded(lobby.game.rules, lobbyRound) + FinishedPlaying.ifNeeded(lobby.game.rules, lobbyRound), ); return { lobby, events, timeouts }; } else { diff --git a/server/src/ts/action/game-action/player/take-back.ts b/server/src/ts/action/game-action/player/take-back.ts index 874d0e30..9875a59f 100644 --- a/server/src/ts/action/game-action/player/take-back.ts +++ b/server/src/ts/action/game-action/player/take-back.ts @@ -1,11 +1,11 @@ -import { InvalidActionError } from "../../../errors/validation"; -import * as Event from "../../../event"; -import * as PlayTakenBack from "../../../events/game-event/play-taken-back"; -import * as Round from "../../../games/game/round"; -import * as Lobby from "../../../lobby"; -import * as Actions from "../../actions"; -import * as Handler from "../../handler"; -import { Player } from "../player"; +import { InvalidActionError } from "../../../errors/validation.js"; +import * as Event from "../../../event.js"; +import * as PlayTakenBack from "../../../events/game-event/play-taken-back.js"; +import type * as Round from "../../../games/game/round.js"; +import type * as Lobby from "../../../lobby.js"; +import * as Actions from "../../actions.js"; +import type * as Handler from "../../handler.js"; +import type { Player } from "../player.js"; /** * A player plays a white card into a round. @@ -25,7 +25,7 @@ class TakeBackActions extends Actions.Implementation< protected handle: Handler.Custom = ( auth, lobby, - action + action, ) => { if (lobby.game.round.verifyStage(action, "Playing")) { const plays = lobby.game.round.plays; diff --git a/server/src/ts/action/game-action/redraw.ts b/server/src/ts/action/game-action/redraw.ts index df90df8b..7de2e6f1 100644 --- a/server/src/ts/action/game-action/redraw.ts +++ b/server/src/ts/action/game-action/redraw.ts @@ -1,11 +1,11 @@ -import { IncorrectUserRoleError } from "../../errors/action-execution-error"; -import { InvalidActionError } from "../../errors/validation"; -import * as Event from "../../event"; -import * as HandRedrawn from "../../events/game-event/hand-redrawn"; -import * as Lobby from "../../lobby"; -import * as Actions from "../actions"; -import * as GameAction from "../game-action"; -import * as Handler from "../handler"; +import { IncorrectUserRoleError } from "../../errors/action-execution-error.js"; +import { InvalidActionError } from "../../errors/validation.js"; +import * as Event from "../../event.js"; +import * as HandRedrawn from "../../events/game-event/hand-redrawn.js"; +import type * as Lobby from "../../lobby.js"; +import * as Actions from "../actions.js"; +import type * as GameAction from "../game-action.js"; +import type * as Handler from "../handler.js"; /** * A player plays a white card into a round. @@ -25,7 +25,7 @@ class RedrawActions extends Actions.Implementation< protected handle: Handler.Custom = ( auth, lobby, - action + action, ) => { const game = lobby.game; const reboot = game.rules.houseRules.reboot; @@ -47,7 +47,7 @@ class RedrawActions extends Actions.Implementation< events: [ Event.additionally( HandRedrawn.of(auth.uid), - new Map([[auth.uid, { hand: player.hand }]]) + new Map([[auth.uid, { hand: player.hand }]]), ), ], }; diff --git a/server/src/ts/action/game-action/set-presence.ts b/server/src/ts/action/game-action/set-presence.ts index 5d0ba15e..521d47f4 100644 --- a/server/src/ts/action/game-action/set-presence.ts +++ b/server/src/ts/action/game-action/set-presence.ts @@ -1,15 +1,15 @@ -import { InvalidActionError } from "../../errors/validation"; -import * as Event from "../../event"; -import * as PlayerPresenceChanged from "../../events/game-event/player-presence-changed"; -import * as Player from "../../games/player"; -import * as Lobby from "../../lobby"; -import { ConstrainedChange } from "../../lobby/change"; -import { ServerState } from "../../server-state"; -import * as User from "../../user"; -import * as Util from "../../util"; -import * as GameAction from "../game-action"; -import * as Handler from "../handler"; -import * as Actions from "./../actions"; +import { InvalidActionError } from "../../errors/validation.js"; +import * as Event from "../../event.js"; +import * as PlayerPresenceChanged from "../../events/game-event/player-presence-changed.js"; +import type * as Player from "../../games/player.js"; +import type * as Lobby from "../../lobby.js"; +import type { ConstrainedChange } from "../../lobby/change.js"; +import type { ServerState } from "../../server-state.js"; +import type * as User from "../../user.js"; +import * as Util from "../../util.js"; +import type * as GameAction from "../game-action.js"; +import type * as Handler from "../handler.js"; +import * as Actions from "./../actions.js"; /** * A player asks to set themself as away. @@ -22,7 +22,7 @@ export interface SetPresence { export const dealWithLostPlayer = ( server: ServerState, lobby: Lobby.WithActiveGame, - playerId: User.Id + playerId: User.Id, ): ConstrainedChange => { const game = lobby.game; @@ -70,7 +70,7 @@ class SetPresenceActions extends Actions.Implementation< auth, lobby, action, - server + server, ) => { const game = lobby.game; const playerId = auth.uid; diff --git a/server/src/ts/action/handler.ts b/server/src/ts/action/handler.ts index f7c80b1f..fffe21a4 100644 --- a/server/src/ts/action/handler.ts +++ b/server/src/ts/action/handler.ts @@ -1,18 +1,18 @@ -import { Action } from "../action"; -import { Change } from "../lobby/change"; -import { Lobby } from "../lobby"; -import * as token from "../user/token"; -import { ServerState } from "../server-state"; +import type { Action } from "../action.js"; +import type { Lobby } from "../lobby.js"; +import type { Change } from "../lobby/change.js"; +import type { ServerState } from "../server-state.js"; +import type * as Token from "../user/token.js"; /** * A handler for a given type of action where the lobby is customised. * This can let us avoid making the same checks down the line. */ export type Custom = ( - auth: token.Claims, + auth: Token.Claims, lobby: L, action: A, - server: ServerState + server: ServerState, ) => Change; /** diff --git a/server/src/ts/action/initial/check-alive.ts b/server/src/ts/action/initial/check-alive.ts index b3baa94a..9e386d37 100644 --- a/server/src/ts/action/initial/check-alive.ts +++ b/server/src/ts/action/initial/check-alive.ts @@ -1,6 +1,6 @@ -import { InvalidActionError } from "../../errors/validation"; -import { Token } from "../../user/token"; -import * as Validation from "../validation.validator"; +import { InvalidActionError } from "../../errors/validation.js"; +import type { Token } from "../../user/token.js"; +import * as Validation from "../validation.validator.js"; /** * Previously obtained tokens to check the validity of. diff --git a/server/src/ts/action/initial/create-lobby.ts b/server/src/ts/action/initial/create-lobby.ts index 8aca944f..d3960500 100644 --- a/server/src/ts/action/initial/create-lobby.ts +++ b/server/src/ts/action/initial/create-lobby.ts @@ -1,6 +1,6 @@ -import { InvalidActionError } from "../../errors/validation"; -import * as Validation from "../validation.validator"; -import { RegisterUser } from "./register-user"; +import { InvalidActionError } from "../../errors/validation.js"; +import * as Validation from "../validation.validator.js"; +import type { RegisterUser } from "./register-user.js"; /** * The details needed to create a new lobby. diff --git a/server/src/ts/action/initial/register-user.ts b/server/src/ts/action/initial/register-user.ts index 494547ca..f9280a9a 100644 --- a/server/src/ts/action/initial/register-user.ts +++ b/server/src/ts/action/initial/register-user.ts @@ -1,6 +1,6 @@ -import { InvalidActionError } from "../../errors/validation"; -import * as User from "../../user"; -import * as Validation from "../validation.validator"; +import { InvalidActionError } from "../../errors/validation.js"; +import type * as User from "../../user.js"; +import * as Validation from "../validation.validator.js"; /** * The details to register a new user for a lobby. diff --git a/server/src/ts/action/leave.ts b/server/src/ts/action/leave.ts index 8518e6b2..9f3530ae 100644 --- a/server/src/ts/action/leave.ts +++ b/server/src/ts/action/leave.ts @@ -1,8 +1,8 @@ -import * as Action from "../action"; -import * as Lobby from "../lobby"; -import * as Actions from "./actions"; -import * as Handler from "./handler"; -import * as Kick from "./privileged/kick"; +import type * as Action from "../action.js"; +import type * as Lobby from "../lobby.js"; +import * as Actions from "./actions.js"; +import type * as Handler from "./handler.js"; +import * as Kick from "./privileged/kick.js"; /** * A player asks to leave the game. @@ -23,7 +23,7 @@ class LeaveActions extends Actions.Implementation< auth, lobby, action, - server + server, ) => Kick.removeUser(auth.uid, lobby, server, "Left"); } diff --git a/server/src/ts/action/privileged.ts b/server/src/ts/action/privileged.ts index b129b06e..c2710573 100644 --- a/server/src/ts/action/privileged.ts +++ b/server/src/ts/action/privileged.ts @@ -1,14 +1,14 @@ -import { Action } from "../action"; -import { UnprivilegedError } from "../errors/action-execution-error"; -import * as Lobby from "../lobby"; -import * as Token from "../user/token"; -import * as Actions from "./actions"; -import * as Configure from "./privileged/configure"; -import * as EndGame from "./privileged/end-game"; -import * as Kick from "./privileged/kick"; -import * as SetPlayerAway from "./privileged/set-player-away"; -import * as SetPrivilege from "./privileged/set-privilege"; -import * as StartGame from "./privileged/start-game"; +import type { Action } from "../action.js"; +import { UnprivilegedError } from "../errors/action-execution-error.js"; +import type * as Lobby from "../lobby.js"; +import type * as Token from "../user/token.js"; +import * as Actions from "./actions.js"; +import * as Configure from "./privileged/configure.js"; +import * as EndGame from "./privileged/end-game.js"; +import * as Kick from "./privileged/kick.js"; +import * as SetPlayerAway from "./privileged/set-player-away.js"; +import * as SetPrivilege from "./privileged/set-privilege.js"; +import * as StartGame from "./privileged/start-game.js"; /** * An action only a privileged user can perform. @@ -34,14 +34,14 @@ class PrivilegedActions extends Actions.Group< SetPlayerAway.actions, SetPrivilege.actions, Kick.actions, - EndGame.actions + EndGame.actions, ); } public limit( auth: Token.Claims, lobby: Lobby.Lobby, - action: Privileged + action: Privileged, ): lobby is Lobby.WithActiveGame { const user = lobby.users[auth.uid]; if (user === undefined || user.privilege !== "Privileged") { diff --git a/server/src/ts/action/privileged/configure.ts b/server/src/ts/action/privileged/configure.ts index 3b2439b5..2a73da92 100644 --- a/server/src/ts/action/privileged/configure.ts +++ b/server/src/ts/action/privileged/configure.ts @@ -1,23 +1,24 @@ import Rfc6902 from "rfc6902"; -import { ReplaceOperation, TestOperation } from "rfc6902/diff"; -import Rfc6902Patch from "rfc6902/patch"; -import * as Actions from "./../actions"; -import { ConfigEditConflictError } from "../../errors/action-execution-error"; -import { InvalidActionError } from "../../errors/validation"; -import * as Event from "../../event"; -import * as Configured from "../../events/lobby-event/configured"; -import * as Rules from "../../games/rules"; -import * as HouseRules from "../../games/rules/house-rules"; -import * as Rando from "../../games/rules/rando"; -import * as Lobby from "../../lobby"; -import * as Config from "../../lobby/config"; -import { GameCode } from "../../lobby/game-code"; -import { Task } from "../../task"; -import { LoadDeckSummary } from "../../task/load-deck-summary"; -import * as Handler from "../handler"; -import { Privileged } from "../privileged"; -import * as Validation from "../validation.validator"; -import { ServerState } from "../../server-state"; +import type { ReplaceOperation, TestOperation } from "rfc6902/diff.js"; +import Rfc6902Patch from "rfc6902/patch.js"; + +import { ConfigEditConflictError } from "../../errors/action-execution-error.js"; +import { InvalidActionError } from "../../errors/validation.js"; +import * as Event from "../../event.js"; +import * as Configured from "../../events/lobby-event/configured.js"; +import type * as Rules from "../../games/rules.js"; +import type * as HouseRules from "../../games/rules/house-rules.js"; +import * as Rando from "../../games/rules/rando.js"; +import type * as Lobby from "../../lobby.js"; +import * as Config from "../../lobby/config.js"; +import type { GameCode } from "../../lobby/game-code.js"; +import type { ServerState } from "../../server-state.js"; +import type { Task } from "../../task.js"; +import { LoadDeckSummary } from "../../task/load-deck-summary.js"; +import type * as Handler from "../handler.js"; +import type { Privileged } from "../privileged.js"; +import * as Validation from "../validation.validator.js"; +import * as Actions from "./../actions.js"; /** * An action to change the configuration of the lobby. @@ -39,7 +40,7 @@ interface Result { function applyRando( lobby: Lobby.Lobby, existing: Rando.Rando, - updated?: Rando.Public + updated?: Rando.Public, ): Result { const events = Rando.change(lobby, existing, updated); return { result: existing, events, tasks: [] }; @@ -48,12 +49,12 @@ function applyRando( function applyHouseRules( lobby: Lobby.Lobby, existing: HouseRules.HouseRules, - updated: HouseRules.Public + updated: HouseRules.Public, ): Result { const { result, events, tasks } = applyRando( lobby, existing.rando, - updated.rando + updated.rando, ); return { result: { ...updated, rando: result }, @@ -65,12 +66,12 @@ function applyHouseRules( function applyRules( lobby: Lobby.Lobby, existing: Rules.Rules, - updated: Rules.Public + updated: Rules.Public, ): Result { const { result, events, tasks } = applyHouseRules( lobby, existing.houseRules, - updated.houseRules + updated.houseRules, ); return { result: { ...updated, houseRules: result }, @@ -84,12 +85,12 @@ function apply( gameCode: GameCode, lobby: Lobby.Lobby, existing: Config.Config, - updated: Config.Public + updated: Config.Public, ): Result { const { result, events, tasks } = applyRules( lobby, existing.rules, - updated.rules + updated.rules, ); const allTasks = [...tasks]; for (const deck of updated.decks) { @@ -119,8 +120,8 @@ const validate = (operation: Rfc6902.Operation): void => { switch (operation.op) { case "add": if ( - operation.value.hasOwnProperty("summary") || - operation.value.hasOwnProperty("failure") + Object.hasOwn(operation.value, "summary") || + Object.hasOwn(operation.value, "failure") ) { throw new InvalidActionError("Can't add summaries or failures."); } @@ -150,7 +151,7 @@ class ConfigureActions extends Actions.Implementation< auth, lobby, action, - server + server, ) => { const version = lobby.config.version; for (const op of action.change) { @@ -176,7 +177,7 @@ class ConfigureActions extends Actions.Implementation< auth.gc, lobby, lobby.config, - validated + validated, ); lobby.config = result; const testVersion: TestOperation = { diff --git a/server/src/ts/action/privileged/end-game.ts b/server/src/ts/action/privileged/end-game.ts index bf593620..fed64b7e 100644 --- a/server/src/ts/action/privileged/end-game.ts +++ b/server/src/ts/action/privileged/end-game.ts @@ -1,9 +1,9 @@ -import * as Actions from "./../actions"; -import * as Event from "../../event"; -import * as GameEnded from "../../events/game-event/game-ended"; -import * as Lobby from "../../lobby"; -import * as Handler from "../handler"; -import { Privileged } from "../privileged"; +import * as Event from "../../event.js"; +import * as GameEnded from "../../events/game-event/game-ended.js"; +import type * as Lobby from "../../lobby.js"; +import type * as Handler from "../handler.js"; +import type { Privileged } from "../privileged.js"; +import * as Actions from "./../actions.js"; /** * End the current game. @@ -21,9 +21,9 @@ class EndGameActions extends Actions.Implementation< protected readonly name = "EndGame"; protected handle: Handler.Custom = ( - auth, + _auth, lobby, - action + _action, ) => { if (lobby.game === undefined || lobby.game.winner !== undefined) { // If we are asked to end a game that isn't started or is already ended, diff --git a/server/src/ts/action/privileged/kick.ts b/server/src/ts/action/privileged/kick.ts index 2921f740..2f01bf74 100644 --- a/server/src/ts/action/privileged/kick.ts +++ b/server/src/ts/action/privileged/kick.ts @@ -1,14 +1,14 @@ -import * as Actions from "./../actions"; -import { InvalidActionError } from "../../errors/validation"; -import * as Event from "../../event"; -import * as PresenceChanged from "../../events/lobby-event/presence-changed"; -import * as Lobby from "../../lobby"; -import { Change } from "../../lobby/change"; -import { ServerState } from "../../server-state"; -import * as Timeout from "../../timeout"; -import * as User from "../../user"; -import * as Handler from "../handler"; -import { Privileged } from "../privileged"; +import { InvalidActionError } from "../../errors/validation.js"; +import * as Event from "../../event.js"; +import * as PresenceChanged from "../../events/lobby-event/presence-changed.js"; +import type * as Lobby from "../../lobby.js"; +import type { Change } from "../../lobby/change.js"; +import type { ServerState } from "../../server-state.js"; +import type * as Timeout from "../../timeout.js"; +import type * as User from "../../user.js"; +import type * as Handler from "../handler.js"; +import type { Privileged } from "../privileged.js"; +import * as Actions from "./../actions.js"; /** * A player asks to leave the game. @@ -22,9 +22,9 @@ export const removeUser = ( userId: User.Id, lobby: Lobby.Lobby, server: ServerState, - leaveReason: PresenceChanged.LeaveReason + leaveReason: PresenceChanged.LeaveReason, ): Change => { - const user = lobby.users[userId]; + const user = lobby.users[userId] as User.User; user.presence = "Left"; if (user.control === "Computer") { @@ -34,7 +34,7 @@ export const removeUser = ( const allEvents = [ Event.targetAllAndPotentiallyClose( PresenceChanged.left(userId, leaveReason), - (id) => id === userId + (id) => id === userId, ), ]; const allTimeouts: Timeout.After[] = []; @@ -57,13 +57,13 @@ export const removeUser = ( const player = game.players[userId]; if (player !== undefined) { addResult( - game.round.czar === userId ? game.startNewRound(server, lobby) : {} + game.round.czar === userId ? game.startNewRound(server, lobby) : {}, ); addResult( game.round.players.has(userId) ? game.removeFromRound(userId, server) - : {} + : {}, ); } } @@ -87,7 +87,7 @@ class KickActions extends Actions.Implementation< auth, lobby, action, - server + server, ) => removeUser(action.user, lobby, server, "Kicked"); } diff --git a/server/src/ts/action/privileged/set-player-away.ts b/server/src/ts/action/privileged/set-player-away.ts index dd048560..b399af54 100644 --- a/server/src/ts/action/privileged/set-player-away.ts +++ b/server/src/ts/action/privileged/set-player-away.ts @@ -1,10 +1,10 @@ -import * as Actions from "./../actions"; -import { InvalidActionError } from "../../errors/validation"; -import * as User from "../../user"; -import * as Lobby from "../../lobby"; -import * as SetPresence from "../game-action/set-presence"; -import * as Handler from "../handler"; -import { Privileged } from "../privileged"; +import { InvalidActionError } from "../../errors/validation.js"; +import type * as Lobby from "../../lobby.js"; +import type * as User from "../../user.js"; +import * as SetPresence from "../game-action/set-presence.js"; +import type * as Handler from "../handler.js"; +import type { Privileged } from "../privileged.js"; +import * as Actions from "./../actions.js"; /** * A privileged user asks to set a given player as away. @@ -26,14 +26,14 @@ class SetPlayerAwayActions extends Actions.Implementation< auth, lobby, action, - server + server, ) => { const game = lobby.game; if (game === undefined) { throw new InvalidActionError("Must be in a game."); } - const user = lobby.users[action.player]; + const user = lobby.users[action.player] as User.User; if (user.control === "Computer") { throw new InvalidActionError("Can't do this with AIs."); } @@ -48,7 +48,7 @@ class SetPlayerAwayActions extends Actions.Implementation< return SetPresence.dealWithLostPlayer( server, lobby as Lobby.WithActiveGame, - playerId + playerId, ); } else { return {}; diff --git a/server/src/ts/action/privileged/set-privilege.ts b/server/src/ts/action/privileged/set-privilege.ts index 3ea2a819..81011284 100644 --- a/server/src/ts/action/privileged/set-privilege.ts +++ b/server/src/ts/action/privileged/set-privilege.ts @@ -1,11 +1,11 @@ -import * as Actions from "./../actions"; -import { InvalidActionError } from "../../errors/validation"; -import * as Event from "../../event"; -import * as PrivilegeChanged from "../../events/lobby-event/privilege-changed"; -import * as Lobby from "../../lobby"; -import * as User from "../../user"; -import * as Handler from "../handler"; -import { Privileged } from "../privileged"; +import { InvalidActionError } from "../../errors/validation.js"; +import * as Event from "../../event.js"; +import * as PrivilegeChanged from "../../events/lobby-event/privilege-changed.js"; +import type * as Lobby from "../../lobby.js"; +import type * as User from "../../user.js"; +import type * as Handler from "../handler.js"; +import type { Privileged } from "../privileged.js"; +import * as Actions from "./../actions.js"; /** * A privileged user asks to change the privilege of another user. @@ -27,9 +27,9 @@ class SetPrivilegeActions extends Actions.Implementation< protected handle: Handler.Custom = ( auth, lobby, - action + action, ) => { - const user = lobby.users[action.user]; + const user = lobby.users[action.user] as User.User; if (user.control === "Computer") { throw new InvalidActionError("Can't do this with AIs."); diff --git a/server/src/ts/action/privileged/start-game.ts b/server/src/ts/action/privileged/start-game.ts index 618ba7ea..c1969cd1 100644 --- a/server/src/ts/action/privileged/start-game.ts +++ b/server/src/ts/action/privileged/start-game.ts @@ -1,8 +1,8 @@ -import * as Actions from "./../actions"; -import * as Lobby from "../../lobby"; -import { StartGame as StartGameTask } from "../../task/start-game"; -import * as Handler from "../handler"; -import { Privileged } from "../privileged"; +import type * as Lobby from "../../lobby.js"; +import { StartGame as StartGameTask } from "../../task/start-game.js"; +import type * as Handler from "../handler.js"; +import type { Privileged } from "../privileged.js"; +import * as Actions from "./../actions.js"; /** * Start a game in the lobby if possible. @@ -25,7 +25,7 @@ class StartGameActions extends Actions.Implementation< tasks: [ new StartGameTask( auth.gc, - lobby.config.decks.map((s) => s.source) + lobby.config.decks.map((s) => s.source), ), ], }); diff --git a/server/src/ts/action/set-user-role.ts b/server/src/ts/action/set-user-role.ts index 0ef08086..748009e7 100644 --- a/server/src/ts/action/set-user-role.ts +++ b/server/src/ts/action/set-user-role.ts @@ -1,14 +1,14 @@ -import { Action } from "../action"; -import * as Event from "../event"; -import * as UserRoleChanged from "../events/lobby-event/user-role-changed"; -import * as Player from "../games/player"; -import * as Lobby from "../lobby"; -import * as Timeout from "../timeout"; -import * as User from "../user"; -import * as Actions from "./actions"; -import * as Handler from "./handler"; -import { UnprivilegedError } from "../errors/action-execution-error"; -import { Privileged } from "./privileged"; +import type { Action } from "../action.js"; +import { UnprivilegedError } from "../errors/action-execution-error.js"; +import * as Event from "../event.js"; +import * as UserRoleChanged from "../events/lobby-event/user-role-changed.js"; +import * as Player from "../games/player.js"; +import type * as Lobby from "../lobby.js"; +import type * as Timeout from "../timeout.js"; +import type * as User from "../user.js"; +import * as Actions from "./actions.js"; +import type * as Handler from "./handler.js"; +import type { Privileged } from "./privileged.js"; /** * A player asks to leave the game. @@ -31,10 +31,10 @@ class SetUserRoleActions extends Actions.Implementation< auth, lobby, action, - server + server, ) => { const userId = action.id === undefined ? auth.uid : action.id; - const targetUser = lobby.users[userId]; + const targetUser = lobby.users[userId] as User.User; const oldRole = targetUser.role; const newRole = action.role; const additionalMap = new Map(); @@ -56,10 +56,10 @@ class SetUserRoleActions extends Actions.Implementation< }; if ( - lobby.users[auth.uid].privilege !== "Privileged" && + lobby.users[auth.uid]?.privilege !== "Privileged" && (userId !== auth.uid || lobby.config.audienceMode) ) { - throw new UnprivilegedError((action as unknown) as Privileged); + throw new UnprivilegedError(action as unknown as Privileged); } if (oldRole !== newRole) { @@ -80,7 +80,7 @@ class SetUserRoleActions extends Actions.Implementation< } } if (newRole === "Player") { - if (!game.players.hasOwnProperty(userId)) { + if (!Object.hasOwn(game.players, userId)) { const hand = game.decks.responses.draw(game.rules.handSize); additionalMap.set(auth.uid, { hand }); game.players[userId] = Player.initial(hand); @@ -92,7 +92,7 @@ class SetUserRoleActions extends Actions.Implementation< events: [ Event.additionally( UserRoleChanged.of(userId, action.role), - additionalMap + additionalMap, ), ...allEvents, ], diff --git a/server/src/ts/action/validation.ts b/server/src/ts/action/validation.ts index 8c642c06..45770ded 100644 --- a/server/src/ts/action/validation.ts +++ b/server/src/ts/action/validation.ts @@ -1,8 +1,8 @@ -import { Action as ActionType } from "../action"; -import { CreateLobby as CreateLobbyType } from "../action/initial/create-lobby"; -import { RegisterUser as RegisterUserType } from "../action/initial/register-user"; -import { CheckAlive as CheckAliveType } from "../action/initial/check-alive"; -import { Public as PublicConfigType } from "../lobby/config"; +import type { Action as ActionType } from "../action.js"; +import type { CheckAlive as CheckAliveType } from "../action/initial/check-alive.js"; +import type { CreateLobby as CreateLobbyType } from "../action/initial/create-lobby.js"; +import type { RegisterUser as RegisterUserType } from "../action/initial/register-user.js"; +import type { Public as PublicConfigType } from "../lobby/config.js"; export type Action = ActionType; export type CreateLobby = CreateLobbyType; diff --git a/server/src/ts/action/validation.validator.ts b/server/src/ts/action/validation.validator.ts index cb5ce178..2704b965 100644 --- a/server/src/ts/action/validation.validator.ts +++ b/server/src/ts/action/validation.validator.ts @@ -1,12 +1,14 @@ -import Ajv from "ajv"; -import addFormats from "ajv-formats"; -import { - RegisterUser, - CreateLobby, +/* eslint-disable */ +import { default as Ajv } from "ajv"; +import { default as addFormats } from "ajv-formats"; + +import type { Action, CheckAlive, + CreateLobby, PublicConfig, -} from "./validation"; + RegisterUser, +} from "./validation.js"; export const ajv = new Ajv({ allErrors: false, @@ -15,9 +17,6 @@ export const ajv = new Ajv({ }); addFormats(ajv, { mode: "full" }); -import metaSchema from "ajv/lib/refs/json-schema-draft-06.json"; -ajv.addMetaSchema(metaSchema); - export { RegisterUser, CreateLobby, Action, CheckAlive, PublicConfig }; export const Schema = { $schema: "http://json-schema.org/draft-07/schema#", @@ -1122,24 +1121,24 @@ export const Schema = { }; ajv.addSchema(Schema, "Schema"); export function validate( - typeName: "RegisterUser" + typeName: "RegisterUser", ): (value: unknown) => RegisterUser; export function validate( - typeName: "CreateLobby" + typeName: "CreateLobby", ): (value: unknown) => CreateLobby; export function validate(typeName: "Action"): (value: unknown) => Action; export function validate( - typeName: "CheckAlive" + typeName: "CheckAlive", ): (value: unknown) => CheckAlive; export function validate( - typeName: "PublicConfig" + typeName: "PublicConfig", ): (value: unknown) => PublicConfig; export function validate(typeName: string): (value: unknown) => any { const validator: any = ajv.getSchema(`Schema#/definitions/${typeName}`); return (value: unknown): any => { if (!validator) { throw new Error( - `No validator defined for Schema#/definitions/${typeName}` + `No validator defined for Schema#/definitions/${typeName}`, ); } @@ -1152,8 +1151,8 @@ export function validate(typeName: string): (value: unknown) => any { ": " + ajv.errorsText( validator.errors!.filter((e: any) => e.keyword !== "if"), - { dataVar: typeName } - ) + { dataVar: typeName }, + ), ); } diff --git a/server/src/ts/cache.ts b/server/src/ts/cache.ts index 87a8438d..06a658e3 100644 --- a/server/src/ts/cache.ts +++ b/server/src/ts/cache.ts @@ -1,7 +1,7 @@ -import * as ServerConfig from "./config"; -import * as Decks from "./games/cards/decks"; -import * as Source from "./games/cards/source"; -import * as Logging from "./logging"; +import type * as ServerConfig from "./config.js"; +import type * as Decks from "./games/cards/decks.js"; +import type * as Source from "./games/cards/source.js"; +import * as Logging from "./logging.js"; /** * A tag is used to check if there is a need to refresh the data in the cache. @@ -44,14 +44,14 @@ export abstract class Cache { Always extends Tagged, Sometimes extends Tagged, Resolver extends Source.Resolver, - Result + Result, >( source: Resolver, getCachedAlways: (source: Resolver) => Promise | undefined>, cacheAlways: (source: Resolver, value: Always) => void, cacheSometimes: (source: Resolver, value: Sometimes) => void, extract: (result: Result) => [Always, Sometimes | undefined], - miss: () => Promise + miss: () => Promise, ): Promise { const cached = await getCachedAlways(source); if (cached !== undefined && !(await this.cacheExpired(source, cached))) { @@ -68,7 +68,7 @@ export abstract class Cache { private async cacheExpired( source: Source.Resolver, - cached: Aged + cached: Aged, ): Promise { if ( cached.age >= Date.now() + this.config.checkAfter && @@ -89,7 +89,7 @@ export abstract class Cache { */ public async getSummary( source: Source.Resolver, - miss: () => Promise + miss: () => Promise, ): Promise { return this.get( source, @@ -97,7 +97,7 @@ export abstract class Cache { this.cacheSummaryBackground.bind(this), this.cacheTemplatesBackground.bind(this), (result) => [result.summary, result.templates], - miss + miss, ); } @@ -111,7 +111,7 @@ export abstract class Cache { */ public async getTemplates( source: Source.Resolver, - miss: () => Promise + miss: () => Promise, ): Promise { return this.get( source, @@ -119,7 +119,7 @@ export abstract class Cache { this.cacheTemplatesBackground.bind(this), this.cacheSummaryBackground.bind(this), (result) => [result.templates, result.summary], - miss + miss, ); } @@ -127,7 +127,7 @@ export abstract class Cache { * Get the given summary from the cache. */ public abstract getCachedSummary( - source: Source.Resolver + source: Source.Resolver, ): Promise | undefined>; /** @@ -135,12 +135,12 @@ export abstract class Cache { */ public abstract cacheSummary( source: Source.Resolver, - summary: Source.Summary + summary: Source.Summary, ): Promise; public cacheSummaryBackground( source: Source.Resolver, - summary: Source.Summary + summary: Source.Summary, ): void { this.cacheSummary(source, summary).catch(Cache.logError); } @@ -149,7 +149,7 @@ export abstract class Cache { * Get the given deck templates from the cache. */ public abstract getCachedTemplates( - source: Source.Resolver + source: Source.Resolver, ): Promise | undefined>; /** @@ -157,12 +157,12 @@ export abstract class Cache { */ public abstract cacheTemplates( source: Source.Resolver, - templates: Decks.Templates + templates: Decks.Templates, ): Promise; public cacheTemplatesBackground( source: Source.Resolver, - templates: Decks.Templates + templates: Decks.Templates, ): void { this.cacheTemplates(source, templates).catch(Cache.logError); } diff --git a/server/src/ts/caches.ts b/server/src/ts/caches.ts index 70e14468..2e6971b4 100644 --- a/server/src/ts/caches.ts +++ b/server/src/ts/caches.ts @@ -1,7 +1,7 @@ -import { PostgresCache } from "./caches/postgres"; -import * as ServerConfig from "./config"; -import { Cache } from "./cache"; -import { InMemoryCache } from "./caches/in-memory"; +import type { Cache } from "./cache.js"; +import { InMemoryCache } from "./caches/in-memory.js"; +import { PostgresCache } from "./caches/postgres.js"; +import type * as ServerConfig from "./config.js"; export async function from(config: ServerConfig.Cache): Promise { switch (config.type) { diff --git a/server/src/ts/caches/in-memory.ts b/server/src/ts/caches/in-memory.ts index 24a593c5..3bdd4af9 100644 --- a/server/src/ts/caches/in-memory.ts +++ b/server/src/ts/caches/in-memory.ts @@ -1,7 +1,7 @@ -import * as Cache from "../cache"; -import * as Config from "../config"; -import * as Decks from "../games/cards/decks"; -import * as Source from "../games/cards/source"; +import * as Cache from "../cache.js"; +import type * as Config from "../config.js"; +import type * as Decks from "../games/cards/decks.js"; +import type * as Source from "../games/cards/source.js"; /** * An in-memory cache. @@ -23,14 +23,14 @@ export class InMemoryCache extends Cache.Cache { } private static key( - source: Source.Resolver + source: Source.Resolver, ): [string, string] { return [source.id(), source.deckId()]; } public async cacheSummary( source: Source.Resolver, - summary: Source.Summary + summary: Source.Summary, ): Promise { this.cache.summaries.set(InMemoryCache.key(source), { cached: summary, @@ -40,7 +40,7 @@ export class InMemoryCache extends Cache.Cache { public async cacheTemplates( source: Source.Resolver, - templates: Decks.Templates + templates: Decks.Templates, ): Promise { this.cache.templates.set(InMemoryCache.key(source), { cached: templates, @@ -49,13 +49,13 @@ export class InMemoryCache extends Cache.Cache { } public async getCachedSummary( - source: Source.Resolver + source: Source.Resolver, ): Promise | undefined> { return this.cache.summaries.get(InMemoryCache.key(source)); } public async getCachedTemplates( - source: Source.Resolver + source: Source.Resolver, ): Promise | undefined> { return this.cache.templates.get(InMemoryCache.key(source)); } diff --git a/server/src/ts/caches/postgres.ts b/server/src/ts/caches/postgres.ts index 045f1fa8..b3c0f5e4 100644 --- a/server/src/ts/caches/postgres.ts +++ b/server/src/ts/caches/postgres.ts @@ -1,12 +1,13 @@ -import Pg from "pg"; -import * as Cache from "../cache"; -import * as Config from "../config"; -import * as Decks from "../games/cards/decks"; -import * as Source from "../games/cards/source"; -import * as Postgres from "../util/postgres"; -import * as Card from "../games/cards/card"; +import type Pg from "pg"; import * as uuid from "uuid"; +import * as Cache from "../cache.js"; +import type * as Config from "../config.js"; +import * as Card from "../games/cards/card.js"; +import type * as Decks from "../games/cards/decks.js"; +import type * as Source from "../games/cards/source.js"; +import * as Postgres from "../util/postgres.js"; + class To1 extends Postgres.Upgrade<0, 1> { public readonly to = 1; @@ -27,7 +28,7 @@ class To0 extends Postgres.Upgrade { public async apply(client: Pg.PoolClient): Promise<0> { await client.query("CREATE SCHEMA mdcache;"); await client.query( - "CREATE TABLE mdcache.meta ( version INTEGER NOT NULL );" + "CREATE TABLE mdcache.meta ( version INTEGER NOT NULL );", ); await client.query(` CREATE TABLE mdcache.decks ( @@ -101,7 +102,7 @@ export class PostgresCache extends Cache.Cache { private readonly pg: Postgres.Postgres; public static async create( - config: Config.PostgreSQLCache + config: Config.PostgreSQLCache, ): Promise { const pg = new Postgres.Postgres("mdcache", config.connection, upgrades); await pg.ensureCurrent(); @@ -116,7 +117,7 @@ export class PostgresCache extends Cache.Cache { public async cacheSummary( source: Source.Resolver, - summary: Source.Summary + summary: Source.Summary, ): Promise { await this.pg.inTransaction(async (client) => { await client.query( @@ -125,7 +126,7 @@ export class PostgresCache extends Cache.Cache { $1, $2 ) ON CONFLICT (source, id) DO NOTHING; `, - [source.id(), source.deckId()] + [source.id(), source.deckId()], ); const details = summary.details; await client.query( @@ -148,14 +149,14 @@ export class PostgresCache extends Cache.Cache { summary.responses, Date.now(), summary.tag, - ] + ], ); }); } public async cacheTemplates( source: Source.Resolver, - templates: Decks.Templates + templates: Decks.Templates, ): Promise { await this.pg.inTransaction(async (client) => { await client.query( @@ -164,7 +165,7 @@ export class PostgresCache extends Cache.Cache { $1, $2, $3, $4 ) ON CONFLICT (source, id) DO UPDATE SET cards_updated=$3, cards_tag=$4; `, - [source.id(), source.deckId(), Date.now(), templates.tag] + [source.id(), source.deckId(), Date.now(), templates.tag], ); for (const call of templates.calls) { @@ -174,7 +175,7 @@ export class PostgresCache extends Cache.Cache { $1, $2, $3 ) `, - [source.id(), source.deckId(), JSON.stringify(call.parts)] + [source.id(), source.deckId(), JSON.stringify(call.parts)], ); } @@ -188,14 +189,14 @@ export class PostgresCache extends Cache.Cache { $1, $2, $3 ) `, - [source.id(), source.deckId(), response.text] + [source.id(), source.deckId(), response.text], ); } }); } public async getCachedSummary( - source: Source.Resolver + source: Source.Resolver, ): Promise | undefined> { return await this.pg.withClient(async (client) => { const result = await client.query( @@ -203,7 +204,7 @@ export class PostgresCache extends Cache.Cache { SELECT name, url, author, language, translator, calls, responses, updated FROM mdcache.summaries WHERE source = $1 AND deck = $2 `, - [source.id(), source.deckId()] + [source.id(), source.deckId()], ); if (result.rowCount > 0) { const row = result.rows[0]; @@ -228,14 +229,14 @@ export class PostgresCache extends Cache.Cache { } public async getCachedTemplates( - source: Source.Resolver + source: Source.Resolver, ): Promise | undefined> { return await this.pg.withClient(async (client) => { const about = await client.query( ` SELECT cards_updated, cards_tag FROM mdcache.decks WHERE source = $1 AND id = $2 `, - [source.id(), source.deckId()] + [source.id(), source.deckId()], ); if (about.rowCount > 0 && about.rows[0]["cards_updated"] !== undefined) { const calls = new Set(); @@ -245,7 +246,7 @@ export class PostgresCache extends Cache.Cache { ` SELECT parts FROM mdcache.calls WHERE source = $1 AND deck = $2 `, - [source.id(), source.deckId()] + [source.id(), source.deckId()], ); for (const c of callsResult.rows) { @@ -260,7 +261,7 @@ export class PostgresCache extends Cache.Cache { ` SELECT text FROM mdcache.responses WHERE source = $1 AND deck = $2 `, - [source.id(), source.deckId()] + [source.id(), source.deckId()], ); for (const r of responsesResult.rows) { diff --git a/server/src/ts/config.ts b/server/src/ts/config.ts index 1fc0424c..f8c3565b 100644 --- a/server/src/ts/config.ts +++ b/server/src/ts/config.ts @@ -1,6 +1,7 @@ import Moment from "moment"; -import * as Util from "./util"; -import * as LobbyConfig from "./lobby/config"; + +import type * as LobbyConfig from "./lobby/config.js"; +import * as Util from "./util.js"; type Duration = UnparsedDuration | ParsedDuration; type UnparsedDuration = string; diff --git a/server/src/ts/errors/action-execution-error.ts b/server/src/ts/errors/action-execution-error.ts index 8d5da033..f6064053 100644 --- a/server/src/ts/errors/action-execution-error.ts +++ b/server/src/ts/errors/action-execution-error.ts @@ -1,17 +1,16 @@ import HttpStatus from "http-status-codes"; -import { Action } from "../action"; -import { GameAction } from "../action/game-action"; -import { Privileged } from "../action/privileged"; -import * as Errors from "../errors"; -import * as Round from "../games/game/round"; -import * as Player from "../games/player"; -import * as User from "../user"; -import * as Source from "../games/cards/source"; - -abstract class ActionExecutionError extends Errors.MassiveDecksError< - Errors.Details -> { - public readonly status: number = HttpStatus.BAD_REQUEST; + +import type { Action } from "../action.js"; +import type { GameAction } from "../action/game-action.js"; +import type { Privileged } from "../action/privileged.js"; +import * as Errors from "../errors.js"; +import type * as Source from "../games/cards/source.js"; +import type * as Round from "../games/game/round.js"; +import type * as Player from "../games/player.js"; +import type * as User from "../user.js"; + +abstract class ActionExecutionError extends Errors.MassiveDecksError { + public override readonly status: number = HttpStatus.BAD_REQUEST; public readonly action: Action; protected constructor(message: string, action: Action) { @@ -26,7 +25,7 @@ export class GameNotStartedError extends ActionExecutionError { public constructor(action: GameAction) { super( `The game must be started for this action:\n ${JSON.stringify(action)}`, - action + action, ); Error.captureStackTrace(this, GameNotStartedError); } @@ -38,13 +37,13 @@ export class GameNotStartedError extends ActionExecutionError { // Could happen if the user has privileges removed. export class UnprivilegedError extends ActionExecutionError { - public readonly status = HttpStatus.FORBIDDEN; + public override readonly status = HttpStatus.FORBIDDEN; public constructor(action: Privileged) { super( `The user does not have the privilege to perform this action:\n` + `${JSON.stringify(action)}`, - action + action, ); Error.captureStackTrace(this, UnprivilegedError); } @@ -67,12 +66,12 @@ export class IncorrectPlayerRoleError extends ActionExecutionError { public constructor( action: Action, role: Player.Role | null, - expected: Player.Role + expected: Player.Role, ) { super( `For this action the player must be ${expected} but is ${role}:\n` + `${JSON.stringify(action)}`, - action + action, ); this.role = role; this.expected = expected; @@ -100,7 +99,7 @@ export class IncorrectUserRoleError extends ActionExecutionError { super( `For this action the user must be ${expected} but is ${role}:\n` + `${JSON.stringify(action)}`, - action + action, ); this.role = role; this.expected = expected; @@ -132,7 +131,7 @@ export class IncorrectRoundStageError extends ActionExecutionError { super( `For this action the round must be in the ${expected} stage but is in ` + `the ${stage} stage:\n ${JSON.stringify(action)}`, - action + action, ); this.stage = stage; this.expected = expected; @@ -153,7 +152,7 @@ interface ConfigEditConflictDetails extends Errors.Details { // Could happen if two users edit the configuration at the same time. export class ConfigEditConflictError extends ActionExecutionError { - public readonly status: number = HttpStatus.CONFLICT; + public override readonly status: number = HttpStatus.CONFLICT; public readonly version: string; public readonly expected: string; @@ -161,7 +160,7 @@ export class ConfigEditConflictError extends ActionExecutionError { super( `The configuration is at version ${expected}, but the client's edit ` + `was made to version ${version}:\n ${JSON.stringify(action)}`, - action + action, ); this.version = version; this.expected = expected; @@ -186,7 +185,7 @@ export class SourceNotFoundError extends ActionExecutionError { public constructor(source: Source.External) { super( `The given deck (${source}) was not found at the source.`, - (undefined as unknown) as Action + undefined as unknown as Action, ); this.source = source; Error.captureStackTrace(this, SourceNotFoundError); @@ -205,7 +204,7 @@ export class SourceServiceError extends ActionExecutionError { public constructor(source: Source.External) { super( `The given deck source (${source.source}) was not available.`, - (undefined as unknown) as Action + undefined as unknown as Action, ); this.source = source; Error.captureStackTrace(this, SourceServiceError); diff --git a/server/src/ts/errors/authentication.ts b/server/src/ts/errors/authentication.ts index 53dd95d8..356ad97b 100644 --- a/server/src/ts/errors/authentication.ts +++ b/server/src/ts/errors/authentication.ts @@ -1,5 +1,6 @@ import HttpStatus from "http-status-codes"; -import * as Errors from "../errors"; + +import * as Errors from "../errors.js"; export type Reason = | "IncorrectIssuer" @@ -14,9 +15,7 @@ export interface Details extends Errors.Details { reason: Reason; } -abstract class AuthenticationFailureError extends Errors.MassiveDecksError< - Details -> { +abstract class AuthenticationFailureError extends Errors.MassiveDecksError
{ public readonly status = HttpStatus.FORBIDDEN; abstract readonly reason: Reason; @@ -38,7 +37,7 @@ export class IncorrectIssuerError extends AuthenticationFailureError { public constructor() { super( "the authentication was not for this server or the server data store " + - "has been wiped" + "has been wiped", ); Error.captureStackTrace(this, IncorrectIssuerError); } diff --git a/server/src/ts/errors/game-state-error.ts b/server/src/ts/errors/game-state-error.ts index 225c1cf7..3ab113c4 100644 --- a/server/src/ts/errors/game-state-error.ts +++ b/server/src/ts/errors/game-state-error.ts @@ -1,9 +1,8 @@ import HttpStatus from "http-status-codes"; -import * as Errors from "../errors"; -export abstract class GameStateError extends Errors.MassiveDecksError< - Errors.Details -> { +import * as Errors from "../errors.js"; + +export abstract class GameStateError extends Errors.MassiveDecksError { public readonly status: number = HttpStatus.INTERNAL_SERVER_ERROR; protected constructor(message: string) { diff --git a/server/src/ts/errors/lobby.ts b/server/src/ts/errors/lobby.ts index 89f03c46..a203466a 100644 --- a/server/src/ts/errors/lobby.ts +++ b/server/src/ts/errors/lobby.ts @@ -1,6 +1,7 @@ import HttpStatus from "http-status-codes"; -import * as Errors from "../errors"; -import { GameCode } from "../lobby/game-code"; + +import * as Errors from "../errors.js"; +import type { GameCode } from "../lobby/game-code.js"; export type Reason = "Closed" | "DoesNotExist"; @@ -10,9 +11,7 @@ export interface Details extends Errors.Details { } // Could happen on user typo. -export abstract class LobbyNotFoundError extends Errors.MassiveDecksError< - Details -> { +export abstract class LobbyNotFoundError extends Errors.MassiveDecksError
{ abstract readonly reason: Reason; public readonly gameCode: GameCode; diff --git a/server/src/ts/errors/registration.ts b/server/src/ts/errors/registration.ts index 3b109032..8b0661fe 100644 --- a/server/src/ts/errors/registration.ts +++ b/server/src/ts/errors/registration.ts @@ -1,5 +1,6 @@ import HttpStatus from "http-status-codes"; -import * as Errors from "../errors"; + +import * as Errors from "../errors.js"; export type Reason = "UsernameAlreadyInUse"; @@ -34,9 +35,7 @@ export interface UsernameDetails { username: string; } -export class UsernameAlreadyInUseError extends RegistrationError< - UsernameDetails -> { +export class UsernameAlreadyInUseError extends RegistrationError { protected readonly reason = "UsernameAlreadyInUse"; private readonly username: string; diff --git a/server/src/ts/errors/validation.ts b/server/src/ts/errors/validation.ts index 85f13638..8f1ee1d8 100644 --- a/server/src/ts/errors/validation.ts +++ b/server/src/ts/errors/validation.ts @@ -1,5 +1,6 @@ import HttpStatus from "http-status-codes"; -import * as Errors from "../errors"; + +import * as Errors from "../errors.js"; export interface Details extends Errors.Details { reason: string; diff --git a/server/src/ts/event.ts b/server/src/ts/event.ts index 3dac8401..393f7455 100644 --- a/server/src/ts/event.ts +++ b/server/src/ts/event.ts @@ -1,12 +1,12 @@ -import { GameEvent } from "./events/game-event"; -import { LobbyEvent } from "./events/lobby-event"; -import { UserEvent } from "./events/user-event"; -import { Player } from "./games/player"; -import { Lobby } from "./lobby"; -import * as Logging from "./logging"; -import * as SocketManager from "./socket-manager"; -import * as User from "./user"; -import { GameCode } from "./lobby/game-code"; +import type { GameEvent } from "./events/game-event.js"; +import type { LobbyEvent } from "./events/lobby-event.js"; +import type { UserEvent } from "./events/user-event.js"; +import type { Player } from "./games/player.js"; +import type { Lobby } from "./lobby.js"; +import type { GameCode } from "./lobby/game-code.js"; +import * as Logging from "./logging.js"; +import type * as SocketManager from "./socket-manager.js"; +import type * as User from "./user.js"; /** * An event send to clients to update them on the state of the game. @@ -18,7 +18,7 @@ export type Event = LobbyEvent | UserEvent | GameEvent; */ export type Distributor = ( lobby: Lobby, - send: (target: User.Id, payload: string, close: boolean) => void + send: (target: User.Id, payload: string, close: boolean) => void, ) => void; /** @@ -60,7 +60,7 @@ export const targetOnly = export const additionally = ( event: T, - additions: Map> + additions: Map>, ): Distributor => (lobby, send) => { const basicRendered = JSON.stringify(event); @@ -86,7 +86,7 @@ export const conditionally = ( event: T, condition: (id: User.Id, user: User.User) => boolean, - addition: Partial + addition: Partial, ): Distributor => (lobby, send) => { const basicRendered = JSON.stringify(event); @@ -106,7 +106,7 @@ export const conditionally = export const playerSpecificAddition = >( event: T, - addition: (id: User.Id, user: User.User, player: Player) => U + addition: (id: User.Id, user: User.User, player: Player) => U, ): Distributor => (lobby, send) => { const basicRendered = JSON.stringify(event); @@ -147,7 +147,7 @@ export const targetAllAndPotentiallyClose = const sendHelper = ( sockets: SocketManager.Sockets, - gameCode: GameCode + gameCode: GameCode, ): ((user: User.Id, serializedEvent: string, close: boolean) => void) => (user, serializedEvent, close) => { try { @@ -179,7 +179,7 @@ export function send( sockets: SocketManager.SocketManager, gameCode: GameCode, lobby: Lobby, - events: Iterable + events: Iterable, ): void { const sendToUser = sendHelper(sockets.sockets, gameCode); for (const event of events) { diff --git a/server/src/ts/events/game-event.ts b/server/src/ts/events/game-event.ts index a4e3bb45..b22aea2d 100644 --- a/server/src/ts/events/game-event.ts +++ b/server/src/ts/events/game-event.ts @@ -1,19 +1,19 @@ -import { GameEnded } from "./game-event/game-ended"; -import { GameStarted } from "./game-event/game-started"; -import { HandRedrawn } from "./game-event/hand-redrawn"; -import { PauseStateChanged } from "./game-event/pause-state-changed"; -import { PlayRevealed } from "./game-event/play-revealed"; -import { PlaySubmitted } from "./game-event/play-submitted"; -import { PlayTakenBack } from "./game-event/play-taken-back"; -import { PlayerPresenceChanged } from "./game-event/player-presence-changed"; -import { RoundFinished } from "./game-event/round-finished"; -import { RoundStarted } from "./game-event/round-started"; -import { PlayingStarted } from "./game-event/playing-started"; -import { StageTimerDone } from "./game-event/stage-timer-done"; -import { StartRevealing } from "./game-event/start-revealing"; -import { StartJudging } from "./game-event/start-judging"; -import { CardDiscarded } from "./game-event/card-discarded"; -import { PlayLiked } from "./game-event/play-liked"; +import type { CardDiscarded } from "./game-event/card-discarded.js"; +import type { GameEnded } from "./game-event/game-ended.js"; +import type { GameStarted } from "./game-event/game-started.js"; +import type { HandRedrawn } from "./game-event/hand-redrawn.js"; +import type { PauseStateChanged } from "./game-event/pause-state-changed.js"; +import type { PlayLiked } from "./game-event/play-liked.js"; +import type { PlayRevealed } from "./game-event/play-revealed.js"; +import type { PlaySubmitted } from "./game-event/play-submitted.js"; +import type { PlayTakenBack } from "./game-event/play-taken-back.js"; +import type { PlayerPresenceChanged } from "./game-event/player-presence-changed.js"; +import type { PlayingStarted } from "./game-event/playing-started.js"; +import type { RoundFinished } from "./game-event/round-finished.js"; +import type { RoundStarted } from "./game-event/round-started.js"; +import type { StageTimerDone } from "./game-event/stage-timer-done.js"; +import type { StartJudging } from "./game-event/start-judging.js"; +import type { StartRevealing } from "./game-event/start-revealing.js"; export type GameEvent = | GameStarted diff --git a/server/src/ts/events/game-event/card-discarded.ts b/server/src/ts/events/game-event/card-discarded.ts index d38774a9..8f3ef380 100644 --- a/server/src/ts/events/game-event/card-discarded.ts +++ b/server/src/ts/events/game-event/card-discarded.ts @@ -1,5 +1,5 @@ -import * as Card from "../../games/cards/card"; -import * as User from "../../user"; +import type * as Card from "../../games/cards/card.js"; +import type * as User from "../../user.js"; /** * Indicates a player has paid to redraw their hand under the Reboot house rule. @@ -14,7 +14,7 @@ export interface CardDiscarded { export const of = ( player: User.Id, card: Card.Response, - replacement?: Card.Response + replacement?: Card.Response, ): CardDiscarded => ({ event: "CardDiscarded", player, diff --git a/server/src/ts/events/game-event/game-ended.ts b/server/src/ts/events/game-event/game-ended.ts index b6fa7cfc..7991b973 100644 --- a/server/src/ts/events/game-event/game-ended.ts +++ b/server/src/ts/events/game-event/game-ended.ts @@ -1,4 +1,4 @@ -import * as User from "../../user"; +import type * as User from "../../user.js"; /** * Indicated a game has ended. diff --git a/server/src/ts/events/game-event/game-started.ts b/server/src/ts/events/game-event/game-started.ts index 20dbd1e8..63f4b655 100644 --- a/server/src/ts/events/game-event/game-started.ts +++ b/server/src/ts/events/game-event/game-started.ts @@ -1,6 +1,6 @@ -import * as Card from "../../games/cards/card"; -import * as Round from "../../games/game/round"; -import * as PublicRound from "../../games/game/round/public"; +import type * as Card from "../../games/cards/card.js"; +import type * as Round from "../../games/game/round.js"; +import type * as PublicRound from "../../games/game/round/public.js"; /** * Indicated a game has started in the lobby. @@ -23,14 +23,14 @@ export interface Playing { export type GameStarted = Base & (Starting | Playing); export const ofPlaying = ( - startedRound: Round.Playing + startedRound: Round.Playing, ): GameStarted & Playing => ({ event: "GameStarted", round: startedRound.public(), }); export const ofStarting = ( - startedRound: Round.Starting + startedRound: Round.Starting, ): GameStarted & Starting => ({ event: "GameStarted", round: startedRound.public(), diff --git a/server/src/ts/events/game-event/hand-redrawn.ts b/server/src/ts/events/game-event/hand-redrawn.ts index 6fd06da1..4707877f 100644 --- a/server/src/ts/events/game-event/hand-redrawn.ts +++ b/server/src/ts/events/game-event/hand-redrawn.ts @@ -1,5 +1,5 @@ -import * as Card from "../../games/cards/card"; -import * as User from "../../user"; +import type * as Card from "../../games/cards/card.js"; +import type * as User from "../../user.js"; /** * Indicates a player has paid to redraw their hand under the Reboot house rule. diff --git a/server/src/ts/events/game-event/play-liked.ts b/server/src/ts/events/game-event/play-liked.ts index 6d801db3..e0f431d4 100644 --- a/server/src/ts/events/game-event/play-liked.ts +++ b/server/src/ts/events/game-event/play-liked.ts @@ -1,4 +1,4 @@ -import * as Play from "../../games/cards/play"; +import type * as Play from "../../games/cards/play.js"; /** * Indicates a play was liked. diff --git a/server/src/ts/events/game-event/play-revealed.ts b/server/src/ts/events/game-event/play-revealed.ts index 2e9b6a3b..ef8238c4 100644 --- a/server/src/ts/events/game-event/play-revealed.ts +++ b/server/src/ts/events/game-event/play-revealed.ts @@ -1,4 +1,4 @@ -import * as Play from "../../games/cards/play"; +import type * as Play from "../../games/cards/play.js"; /** * Indicates the czar revealed a play for the round. diff --git a/server/src/ts/events/game-event/play-submitted.ts b/server/src/ts/events/game-event/play-submitted.ts index c36754cc..0dbd9e1c 100644 --- a/server/src/ts/events/game-event/play-submitted.ts +++ b/server/src/ts/events/game-event/play-submitted.ts @@ -1,4 +1,4 @@ -import * as User from "../../user"; +import type * as User from "../../user.js"; /** * Indicates a player has submitted a play for the round. diff --git a/server/src/ts/events/game-event/play-taken-back.ts b/server/src/ts/events/game-event/play-taken-back.ts index 73db111f..24ed9d3e 100644 --- a/server/src/ts/events/game-event/play-taken-back.ts +++ b/server/src/ts/events/game-event/play-taken-back.ts @@ -1,4 +1,4 @@ -import * as User from "../../user"; +import type * as User from "../../user.js"; /** * Indicates a player has taken back their play for the round. diff --git a/server/src/ts/events/game-event/player-presence-changed.ts b/server/src/ts/events/game-event/player-presence-changed.ts index 5827718a..bdbc96bb 100644 --- a/server/src/ts/events/game-event/player-presence-changed.ts +++ b/server/src/ts/events/game-event/player-presence-changed.ts @@ -1,4 +1,4 @@ -import * as User from "../../user"; +import type * as User from "../../user.js"; /** * An event for when connection state for a user changes. diff --git a/server/src/ts/events/game-event/playing-started.ts b/server/src/ts/events/game-event/playing-started.ts index 2e8e7c90..4c4a32f2 100644 --- a/server/src/ts/events/game-event/playing-started.ts +++ b/server/src/ts/events/game-event/playing-started.ts @@ -1,5 +1,5 @@ -import * as Card from "../../games/cards/card"; -import * as Round from "../../games/game/round"; +import type * as Card from "../../games/cards/card.js"; +import type * as Round from "../../games/game/round.js"; /** * If there was a Starting phase, this is used to advance to the playing phase. @@ -13,7 +13,7 @@ export interface PlayingStarted { export const of = ( round: Round.Playing, - drawn?: Card.Response[] + drawn?: Card.Response[], ): PlayingStarted => ({ event: "PlayingStarted", id: round.id.toString(), diff --git a/server/src/ts/events/game-event/round-finished.ts b/server/src/ts/events/game-event/round-finished.ts index c2728279..860c9648 100644 --- a/server/src/ts/events/game-event/round-finished.ts +++ b/server/src/ts/events/game-event/round-finished.ts @@ -1,6 +1,6 @@ -import * as GameRound from "../../games/game/round"; -import * as PublicRound from "../../games/game/round/public"; -import * as User from "../../user"; +import type * as GameRound from "../../games/game/round.js"; +import type * as PublicRound from "../../games/game/round/public.js"; +import type * as User from "../../user.js"; /** * Indicates players have finished playing into the round and now the czar diff --git a/server/src/ts/events/game-event/round-started.ts b/server/src/ts/events/game-event/round-started.ts index 2869da82..0ea33c4d 100644 --- a/server/src/ts/events/game-event/round-started.ts +++ b/server/src/ts/events/game-event/round-started.ts @@ -1,6 +1,6 @@ -import * as Card from "../../games/cards/card"; -import * as Round from "../../games/game/round"; -import * as User from "../../user"; +import type * as Card from "../../games/cards/card.js"; +import type * as Round from "../../games/game/round.js"; +import type * as User from "../../user.js"; export interface Base { event: "RoundStarted"; @@ -19,7 +19,7 @@ export type RoundStarted = Base & Call; export const of = ( round: Round.Round, - drawn?: Card.Response[] + drawn?: Card.Response[], ): RoundStarted => ({ event: "RoundStarted", id: round.id.toString(), diff --git a/server/src/ts/events/game-event/stage-timer-done.ts b/server/src/ts/events/game-event/stage-timer-done.ts index d4eedae8..30b247dc 100644 --- a/server/src/ts/events/game-event/stage-timer-done.ts +++ b/server/src/ts/events/game-event/stage-timer-done.ts @@ -1,4 +1,4 @@ -import * as Round from "../../games/game/round"; +import type * as Round from "../../games/game/round.js"; /** * Indicates that the stage timer has completed. diff --git a/server/src/ts/events/game-event/start-judging.ts b/server/src/ts/events/game-event/start-judging.ts index a515ec2d..84810253 100644 --- a/server/src/ts/events/game-event/start-judging.ts +++ b/server/src/ts/events/game-event/start-judging.ts @@ -1,6 +1,6 @@ -import * as Play from "../../games/cards/play"; -import * as StartRevealing from "./start-revealing"; -import * as Card from "../../games/cards/card"; +import type * as Card from "../../games/cards/card.js"; +import type * as Play from "../../games/cards/play.js"; +import type * as StartRevealing from "./start-revealing.js"; /** * Indicates the czar has finished revealing the plays and is now picking a winner. @@ -17,7 +17,7 @@ export interface StartJudging extends StartRevealing.AfterPlaying { export const of = ( plays?: Play.Revealed[], played?: Play.Id, - drawn?: Card.Response[] + drawn?: Card.Response[], ): StartJudging => ({ event: "StartJudging", plays, diff --git a/server/src/ts/events/game-event/start-revealing.ts b/server/src/ts/events/game-event/start-revealing.ts index e3b30a93..d2288291 100644 --- a/server/src/ts/events/game-event/start-revealing.ts +++ b/server/src/ts/events/game-event/start-revealing.ts @@ -1,5 +1,5 @@ -import * as Play from "../../games/cards/play"; -import * as Card from "../../games/cards/card"; +import type * as Card from "../../games/cards/card.js"; +import type * as Play from "../../games/cards/play.js"; /** * Indicates players have finished playing into the round and now the czar @@ -27,7 +27,7 @@ export interface AfterPlaying { export const of = ( plays: Play.Id[], played?: Play.Id, - drawn?: Card.Response[] + drawn?: Card.Response[], ): StartRevealing => ({ event: "StartRevealing", plays, diff --git a/server/src/ts/events/lobby-event.ts b/server/src/ts/events/lobby-event.ts index 7239c0bb..244546f9 100644 --- a/server/src/ts/events/lobby-event.ts +++ b/server/src/ts/events/lobby-event.ts @@ -1,9 +1,9 @@ -import { Configured } from "./lobby-event/configured"; -import { ConnectionChanged } from "./lobby-event/connection-changed"; -import { ErrorEncountered } from "./lobby-event/error-encountered"; -import { PresenceChanged } from "./lobby-event/presence-changed"; -import { PrivilegeChanged } from "./lobby-event/privilege-changed"; -import { UserRoleChanged } from "./lobby-event/user-role-changed"; +import type { Configured } from "./lobby-event/configured.js"; +import type { ConnectionChanged } from "./lobby-event/connection-changed.js"; +import type { ErrorEncountered } from "./lobby-event/error-encountered.js"; +import type { PresenceChanged } from "./lobby-event/presence-changed.js"; +import type { PrivilegeChanged } from "./lobby-event/privilege-changed.js"; +import type { UserRoleChanged } from "./lobby-event/user-role-changed.js"; export type LobbyEvent = | Configured diff --git a/server/src/ts/events/lobby-event/configured.ts b/server/src/ts/events/lobby-event/configured.ts index 53340225..65655aad 100644 --- a/server/src/ts/events/lobby-event/configured.ts +++ b/server/src/ts/events/lobby-event/configured.ts @@ -1,4 +1,4 @@ -import Rfc6902 from "rfc6902"; +import type Rfc6902 from "rfc6902"; /** * A change was made to the configuration for the lobby. diff --git a/server/src/ts/events/lobby-event/connection-changed.ts b/server/src/ts/events/lobby-event/connection-changed.ts index e8779c1f..5dd35029 100644 --- a/server/src/ts/events/lobby-event/connection-changed.ts +++ b/server/src/ts/events/lobby-event/connection-changed.ts @@ -1,4 +1,4 @@ -import * as User from "../../user"; +import type * as User from "../../user.js"; /** * An event for when connection state for a user changes. diff --git a/server/src/ts/events/lobby-event/error-encountered.ts b/server/src/ts/events/lobby-event/error-encountered.ts index 7188e89e..f5973e96 100644 --- a/server/src/ts/events/lobby-event/error-encountered.ts +++ b/server/src/ts/events/lobby-event/error-encountered.ts @@ -1,4 +1,4 @@ -import * as Errors from "../../errors"; +import type * as Errors from "../../errors.js"; /** * An error occurred in the lobby. diff --git a/server/src/ts/events/lobby-event/presence-changed.ts b/server/src/ts/events/lobby-event/presence-changed.ts index 79fc2064..97f3244e 100644 --- a/server/src/ts/events/lobby-event/presence-changed.ts +++ b/server/src/ts/events/lobby-event/presence-changed.ts @@ -1,4 +1,4 @@ -import * as User from "../../user"; +import type * as User from "../../user.js"; /** * An event for when connection state for a user changes. diff --git a/server/src/ts/events/lobby-event/privilege-changed.ts b/server/src/ts/events/lobby-event/privilege-changed.ts index 7e6d071e..d0a14be6 100644 --- a/server/src/ts/events/lobby-event/privilege-changed.ts +++ b/server/src/ts/events/lobby-event/privilege-changed.ts @@ -1,4 +1,4 @@ -import * as User from "../../user"; +import type * as User from "../../user.js"; /** * Indicates a user's level of privilege has changed. @@ -11,7 +11,7 @@ export interface PrivilegeChanged { export const of = ( user: User.Id, - privilege: User.Privilege + privilege: User.Privilege, ): PrivilegeChanged => ({ event: "PrivilegeChanged", user, diff --git a/server/src/ts/events/lobby-event/user-role-changed.ts b/server/src/ts/events/lobby-event/user-role-changed.ts index 90550a16..59dca86d 100644 --- a/server/src/ts/events/lobby-event/user-role-changed.ts +++ b/server/src/ts/events/lobby-event/user-role-changed.ts @@ -1,5 +1,5 @@ -import * as Card from "../../games/cards/card"; -import * as User from "../../user"; +import type * as Card from "../../games/cards/card.js"; +import type * as User from "../../user.js"; /** * Indicates the role for a user has changed. diff --git a/server/src/ts/events/user-event.ts b/server/src/ts/events/user-event.ts index a1bc99e6..b8d44d01 100644 --- a/server/src/ts/events/user-event.ts +++ b/server/src/ts/events/user-event.ts @@ -1,4 +1,4 @@ -import { Sync } from "./user-event/sync"; +import type { Sync } from "./user-event/sync.js"; /** * An event that should be sent to all users. diff --git a/server/src/ts/events/user-event/sync.ts b/server/src/ts/events/user-event/sync.ts index 7bffaffe..9858bc8a 100644 --- a/server/src/ts/events/user-event/sync.ts +++ b/server/src/ts/events/user-event/sync.ts @@ -1,7 +1,7 @@ -import * as Card from "../../games/cards/card"; -import { Hand } from "../../games/cards/hand"; -import * as Lobby from "../../lobby"; -import { LikeDetail } from "../../games/game/round/public"; +import type * as Card from "../../games/cards/card.js"; +import type { Hand } from "../../games/cards/hand.js"; +import type { LikeDetail } from "../../games/game/round/public.js"; +import type * as Lobby from "../../lobby.js"; /** * Synchronise the game state. @@ -21,7 +21,7 @@ export const of = ( hand?: Hand, play?: Card.Id[], likeDetail?: LikeDetail, - calls?: Card.Call[] + calls?: Card.Call[], ): Sync => ({ event: "Sync", state, diff --git a/server/src/ts/games/cards/card.ts b/server/src/ts/games/cards/card.ts index 991fecac..4d66e445 100644 --- a/server/src/ts/games/cards/card.ts +++ b/server/src/ts/games/cards/card.ts @@ -1,7 +1,8 @@ import * as uuid from "uuid"; import wu from "wu"; -import { Source } from "./source"; -import { Custom } from "./sources/custom"; + +import type { Source } from "./source.js"; +import type { Custom } from "./sources/custom.js"; /** * A game card. @@ -41,7 +42,7 @@ export type CustomCard = TCard & { source: Custom }; * If the response is a custom one, and therefore mutable. */ export const isCustom = ( - card: TCard + card: TCard, ): card is CustomCard => card.source.source == "Custom"; /** A unique id for an instance of a card.*/ @@ -65,10 +66,10 @@ export interface Styled { } export const isSlot = (part: Part): part is Slot => - typeof part !== "string" && !part.hasOwnProperty("text"); + typeof part !== "string" && !Object.hasOwn(part, "text"); export const isStyled = (part: Part): part is Styled => - typeof part !== "string" && part.hasOwnProperty("text"); + typeof part !== "string" && Object.hasOwn(part, "text"); /** Either text or a slot.*/ export type Part = string | Styled | Slot; @@ -96,11 +97,11 @@ export const isResponse = (card: Card): card is Response => export const slotCount = (call: Call | Part[][]): number => { let next = 0; const indices = wu( - call.hasOwnProperty("parts") ? (call as Call).parts : (call as Part[][]) + Object.hasOwn(call, "parts") ? (call as Call).parts : (call as Part[][]), ) .flatten(true) .concatMap((part) => - isSlot(part) ? [part.index !== undefined ? part.index : next++] : [] + isSlot(part) ? [part.index !== undefined ? part.index : next++] : [], ); return new Set(indices).size; }; diff --git a/server/src/ts/games/cards/decks.ts b/server/src/ts/games/cards/decks.ts index 16c44119..b7bb09c0 100644 --- a/server/src/ts/games/cards/decks.ts +++ b/server/src/ts/games/cards/decks.ts @@ -1,8 +1,9 @@ import wu from "wu"; -import * as Cache from "../../cache"; -import { OutOfCardsError } from "../../errors/game-state-error"; -import * as Util from "../../util"; -import * as Card from "./card"; + +import type * as Cache from "../../cache.js"; +import { OutOfCardsError } from "../../errors/game-state-error.js"; +import * as Util from "../../util.js"; +import * as Card from "./card.js"; const union = (sets: Iterable>): Set => { const result = new Set(); @@ -42,6 +43,10 @@ export abstract class Deck { this.discarded.add(card); } + public draw(cards: 1): [C]; + public draw(cards: 2): [C, C]; + public draw(cards: 3): [C, C, C]; + public draw(cards: number): C[]; public draw(cards: number): C[] { const cardsLeft = this.cards.length; const toDraw = Math.min(cardsLeft, cards); @@ -54,6 +59,10 @@ export abstract class Deck { } } + public replace(...cards: [C]): [C]; + public replace(...cards: [C, C]): [C, C]; + public replace(...cards: [C, C, C]): [C, C, C]; + public replace(...cards: C[]): C[]; public replace(...cards: C[]): C[] { this.discard(cards); return this.draw(cards.length); @@ -95,7 +104,7 @@ export class Calls extends Deck { * (in the case of custom cards). */ export class Responses extends Deck { - protected discardSingle(card: Card.Response): void { + protected override discardSingle(card: Card.Response): void { // We duplicate the card here so we don't damage any references to it hanging around elsewhere (e.g: history). this.discarded.add({ ...card, @@ -105,7 +114,7 @@ export class Responses extends Deck { } public static fromTemplates( - template: Iterable> + template: Iterable>, ): Responses { const deck = new Responses([], union(template)); deck.reshuffle(); @@ -141,6 +150,6 @@ export interface Templates extends Cache.Tagged { export const decks = (templates: Iterable): Decks => ({ calls: Calls.fromTemplates(wu(templates).map((template) => template.calls)), responses: Responses.fromTemplates( - wu(templates).map((template) => template.responses) + wu(templates).map((template) => template.responses), ), }); diff --git a/server/src/ts/games/cards/hand.ts b/server/src/ts/games/cards/hand.ts index 67fe38b9..10aa549c 100644 --- a/server/src/ts/games/cards/hand.ts +++ b/server/src/ts/games/cards/hand.ts @@ -1,4 +1,4 @@ -import * as Card from "./card"; +import type * as Card from "./card.js"; /** * A hand of cards. diff --git a/server/src/ts/games/cards/play.ts b/server/src/ts/games/cards/play.ts index f942c544..755e2200 100644 --- a/server/src/ts/games/cards/play.ts +++ b/server/src/ts/games/cards/play.ts @@ -1,5 +1,6 @@ import * as uuid from "uuid"; -import { Response } from "./card"; + +import type { Response } from "./card.js"; /** A series of cards played into a round.*/ export type Play = Response[]; diff --git a/server/src/ts/games/cards/source.ts b/server/src/ts/games/cards/source.ts index f8851a69..54fa19da 100644 --- a/server/src/ts/games/cards/source.ts +++ b/server/src/ts/games/cards/source.ts @@ -1,10 +1,10 @@ -import * as Cache from "../../cache"; -import * as Decks from "./decks"; -import { Custom } from "./sources/custom"; -import { Generated } from "./sources/generated"; -import { BuiltIn } from "./sources/builtIn"; -import { ManyDecks } from "./sources/many-decks"; -import { JsonAgainstHumanity } from "./sources/json-against-humanity"; +import type * as Cache from "../../cache.js"; +import type * as Decks from "./decks.js"; +import type { BuiltIn } from "./sources/builtIn.js"; +import type { Custom } from "./sources/custom.js"; +import type { Generated } from "./sources/generated.js"; +import type { JsonAgainstHumanity } from "./sources/json-against-humanity.js"; +import type { ManyDecks } from "./sources/many-decks.js"; /** * A source for a card or deck. @@ -73,7 +73,7 @@ export interface AtLeastTemplates { * A resolver that only allows access to properties that don't require store * access. */ -export interface LimitedResolver { +export interface LimitedResolver { id: () => string; deckId: () => string; loadingDetails: () => Details; @@ -83,8 +83,7 @@ export interface LimitedResolver { /** * Resolve information about the given source. */ -export abstract class Resolver - implements LimitedResolver { +export abstract class Resolver implements LimitedResolver { /** * The source in question. */ @@ -115,13 +114,13 @@ export abstract class Resolver * Note that if you have a fresh summary, you should check if that has a * tag first. */ - public abstract getTag(): Promise; + public abstract getTag(): Promise; /** * The summary for the source, and potentially the templates if efficient to * return both. */ - public abstract atLeastSummary(): Promise; + public abstract atLeastSummary(): Promise; /** * The summary for the source. @@ -141,7 +140,7 @@ export abstract class Resolver * The deck templates for the source, and potentially the summary if * efficient to return both. */ - public abstract atLeastTemplates(): Promise; + public abstract atLeastTemplates(): Promise; /** * The deck templates for the source. @@ -202,7 +201,7 @@ export class CachedResolver extends Resolver { return { summary: await this.cache.getSummary( this.resolver, - async () => await this.resolver.atLeastSummary() + async () => await this.resolver.atLeastSummary(), ), }; } @@ -211,7 +210,7 @@ export class CachedResolver extends Resolver { return { templates: await this.cache.getTemplates( this.resolver, - async () => await this.resolver.atLeastTemplates() + async () => await this.resolver.atLeastTemplates(), ), }; } @@ -250,6 +249,6 @@ export interface MetaResolver { * If the source should be cached. */ readonly cache: boolean; - limitedResolver(source: S): LimitedResolver; + limitedResolver(source: S): LimitedResolver; resolver(source: S): Resolver; } diff --git a/server/src/ts/games/cards/sources.ts b/server/src/ts/games/cards/sources.ts index a9868352..85843efe 100644 --- a/server/src/ts/games/cards/sources.ts +++ b/server/src/ts/games/cards/sources.ts @@ -1,13 +1,13 @@ -import { Cache } from "../../cache"; -import * as Util from "../../util"; -import * as Source from "./source"; -import * as Player from "./sources/custom"; -import * as Config from "../../config"; -import * as BuiltIn from "./sources/builtIn"; -import { SourceNotFoundError } from "../../errors/action-execution-error"; -import * as ManyDecks from "./sources/many-decks"; -import * as JsonAgainstHumanity from "./sources/json-against-humanity"; -import * as Generated from "./sources/generated"; +import type { Cache } from "../../cache.js"; +import type * as Config from "../../config.js"; +import { SourceNotFoundError } from "../../errors/action-execution-error.js"; +import * as Util from "../../util.js"; +import * as Source from "./source.js"; +import * as BuiltIn from "./sources/builtIn.js"; +import * as Player from "./sources/custom.js"; +import * as Generated from "./sources/generated.js"; +import * as JsonAgainstHumanity from "./sources/json-against-humanity.js"; +import * as ManyDecks from "./sources/many-decks.js"; async function loadIfEnabled( config: Config | undefined, diff --git a/server/src/ts/games/cards/sources/builtIn.ts b/server/src/ts/games/cards/sources/builtIn.ts index a9749e16..9bad7142 100644 --- a/server/src/ts/games/cards/sources/builtIn.ts +++ b/server/src/ts/games/cards/sources/builtIn.ts @@ -1,15 +1,16 @@ -import * as Source from "../source"; -import * as Decks from "../decks"; -import JSON5 from "json5"; import { promises as fs } from "fs"; -import * as Config from "../../../config"; +import JSON5 from "json5"; import * as path from "path"; -import { Part } from "../card"; -import * as Card from "../card"; + +import type * as Config from "../../../config.js"; import { SourceNotFoundError, SourceServiceError, -} from "../../../errors/action-execution-error"; +} from "../../../errors/action-execution-error.js"; +import type { Part } from "../card.js"; +import * as Card from "../card.js"; +import type * as Decks from "../decks.js"; +import * as Source from "../source.js"; const extension = ".deck.json5"; @@ -56,7 +57,7 @@ export class Resolver extends Source.Resolver { config: Config.BuiltIn, source: BuiltIn, summary?: Source.Summary, - deck?: BuiltInDeck + deck?: BuiltInDeck, ) { super(); this.config = config; @@ -143,14 +144,14 @@ export class MetaResolver implements Source.MetaResolver { public constructor( config: Config.BuiltIn, summaries: Map, - decks: Map + decks: Map, ) { this.config = config; this.summaries = summaries; this.decks = decks; } - limitedResolver(source: BuiltIn): Source.LimitedResolver { + limitedResolver(source: BuiltIn): Source.LimitedResolver { return this.resolver(source); } @@ -185,7 +186,9 @@ export async function load(config: Config.BuiltIn): Promise { for (const id of config.decks) { const rawDeck = JSON5.parse( - (await fs.readFile(path.join(config.basePath, id + extension))).toString() + ( + await fs.readFile(path.join(config.basePath, id + extension)) + ).toString(), ) as BuiltInDeck; summaries.set(id, { details: { diff --git a/server/src/ts/games/cards/sources/custom.ts b/server/src/ts/games/cards/sources/custom.ts index 7c1a9c7e..6a50e13f 100644 --- a/server/src/ts/games/cards/sources/custom.ts +++ b/server/src/ts/games/cards/sources/custom.ts @@ -1,4 +1,4 @@ -import * as Source from "../source"; +import type * as Source from "../source.js"; /** * A source for custom cards made during the game. diff --git a/server/src/ts/games/cards/sources/generated.ts b/server/src/ts/games/cards/sources/generated.ts index 0b96fb3b..1baa43fc 100644 --- a/server/src/ts/games/cards/sources/generated.ts +++ b/server/src/ts/games/cards/sources/generated.ts @@ -1,4 +1,4 @@ -import * as Source from "../source"; +import type * as Source from "../source.js"; /** * A source for cards generated during the game for reasons such as house rules. diff --git a/server/src/ts/games/cards/sources/json-against-humanity.ts b/server/src/ts/games/cards/sources/json-against-humanity.ts index 4479f988..4867f891 100644 --- a/server/src/ts/games/cards/sources/json-against-humanity.ts +++ b/server/src/ts/games/cards/sources/json-against-humanity.ts @@ -1,9 +1,10 @@ -import * as Source from "../source"; -import http, { AxiosRequestConfig } from "axios"; -import * as Config from "../../../config"; -import { SourceNotFoundError } from "../../../errors/action-execution-error"; -import * as Decks from "../decks"; -import * as Card from "../card"; +import { AxiosRequestConfig, default as Axios } from "axios"; + +import type * as Config from "../../../config.js"; +import { SourceNotFoundError } from "../../../errors/action-execution-error.js"; +import * as Card from "../card.js"; +import type * as Decks from "../decks.js"; +import * as Source from "../source.js"; /** * From JSON Against Humanity (https://crhallberg.com/cah/) @@ -44,7 +45,7 @@ function* introduceSlots(line: string): Iterable { yield nextSlot; } yield part; - const last = part.trimRight().substr(-1); + const last = part.trimEnd().slice(-1); if (part === "" || endsSentence.has(last)) { nextSlot = { transform: "Capitalize" }; } else { @@ -56,7 +57,7 @@ function* introduceSlots(line: string): Iterable { function rawDeckToSummaryAndTemplates( raw: Packs, pack: Pack, - id: string + id: string, ): { summary: Source.Summary; templates: Decks.Templates; @@ -67,7 +68,7 @@ function rawDeckToSummaryAndTemplates( }; function call(index: number): Card.Call { - const from = raw.black[index]; + const from = raw.black[index] as { text: string; pick: number }; const parts = from.text.split("\n").map((t) => [...introduceSlots(t)]); const slots = Card.slotCount(parts); const extraSlots = Math.max(0, from.pick - slots); @@ -79,7 +80,7 @@ function rawDeckToSummaryAndTemplates( } function response(index: number): Card.Response { - const from = raw.white[index]; + const from = raw.white[index] as string; const stripped = from.replace("\n", ""); return { id: Card.id(), @@ -113,7 +114,7 @@ export class Resolver extends Source.Resolver { source: JsonAgainstHumanity, config: Config.JsonAgainstHumanity, summary: Source.Summary, - templates: Decks.Templates + templates: Decks.Templates, ) { super(); this.source = source; @@ -179,7 +180,7 @@ export class MetaResolver implements Source.MetaResolver { const protoOrder: [string, Pack][] = []; for (let index = 0; index < decks.packs.length; index++) { const id = index.toString(); - const pack = decks.packs[index]; + const pack = decks.packs[index] as Pack; this.decks.set(id, rawDeckToSummaryAndTemplates(decks, pack, id)); protoOrder.push([id, pack]); } @@ -189,7 +190,7 @@ export class MetaResolver implements Source.MetaResolver { private static compare( [_idA, a]: [string, Pack], - [_idB, b]: [string, Pack] + [_idB, b]: [string, Pack], ): number { return MetaResolver.boolCompare(a, b, (v) => v.official); } @@ -227,7 +228,7 @@ export class MetaResolver implements Source.MetaResolver { } export const load = async ( - config: Config.JsonAgainstHumanity + config: Config.JsonAgainstHumanity, ): Promise => { const httpConfig: AxiosRequestConfig = { method: "GET", @@ -235,6 +236,6 @@ export const load = async ( responseType: "json", }; - const data = await http.get("", httpConfig); + const data = await Axios.get("", httpConfig); return new MetaResolver(config, data.data); }; diff --git a/server/src/ts/games/cards/sources/many-decks.ts b/server/src/ts/games/cards/sources/many-decks.ts index 257631ec..6e1e17c9 100644 --- a/server/src/ts/games/cards/sources/many-decks.ts +++ b/server/src/ts/games/cards/sources/many-decks.ts @@ -1,16 +1,21 @@ -import * as Source from "../source"; +import { + AxiosError, + AxiosInstance, + AxiosRequestConfig, + default as Axios, +} from "axios"; import genericPool from "generic-pool"; -import http, { AxiosError, AxiosInstance, AxiosRequestConfig } from "axios"; -import * as Config from "../../../config"; import HttpStatus from "http-status-codes"; +import JSON5 from "json5"; + +import type * as Config from "../../../config.js"; import { SourceNotFoundError, SourceServiceError, -} from "../../../errors/action-execution-error"; -import * as Decks from "../decks"; -import JSON5 from "json5"; -import * as Card from "../card"; -import { MassiveDecksError } from "../../../errors"; +} from "../../../errors/action-execution-error.js"; +import * as Card from "../card.js"; +import type * as Decks from "../decks.js"; +import * as Source from "../source.js"; /** * A source that just tries to load an arbitrary URL. @@ -32,7 +37,7 @@ export class Resolver extends Source.Resolver { public constructor( source: ManyDecks, config: Config.ManyDecks, - connectionPool: genericPool.Pool + connectionPool: genericPool.Pool, ) { super(); this.source = source; @@ -146,12 +151,12 @@ export class MetaResolver implements Source.MetaResolver { this.connectionPool = genericPool.createPool( { - create: async () => http.create(httpConfig), + create: async () => Axios.create(httpConfig), destroy: async (_) => { // Do nothing. }, }, - { max: config.simultaneousConnections } + { max: config.simultaneousConnections }, ); } diff --git a/server/src/ts/games/game.ts b/server/src/ts/games/game.ts index 87f66793..85e83c06 100644 --- a/server/src/ts/games/game.ts +++ b/server/src/ts/games/game.ts @@ -1,28 +1,28 @@ import wu from "wu"; -import { InvalidActionError } from "../errors/validation"; -import * as Event from "../event"; -import * as GameStarted from "../events/game-event/game-started"; -import * as PauseStateChanged from "../events/game-event/pause-state-changed"; -import * as PlaySubmitted from "../events/game-event/play-submitted"; -import * as RoundStarted from "../events/game-event/round-started"; -import * as PlayingStarted from "../events/game-event/playing-started"; -import { Lobby } from "../lobby"; -import { ServerState } from "../server-state"; -import * as Timeout from "../timeout"; -import * as FinishedPlaying from "../timeout/finished-playing"; -import * as RoundStageTimerDone from "../timeout/round-stage-timer-done"; -import * as User from "../user"; -import * as Util from "../util"; -import * as Card from "./cards/card"; -import * as Decks from "./cards/decks"; -import * as Play from "./cards/play"; -import * as Round from "./game/round"; -import * as PublicRound from "./game/round/public"; -import { StoredPlay } from "./game/round/stored-play"; -import * as Player from "./player"; -import * as Rules from "./rules"; -import * as HappyEnding from "./rules/happy-ending"; -import * as CzarChoices from "./rules/czar-choices"; + +import { InvalidActionError } from "../errors/validation.js"; +import * as Event from "../event.js"; +import * as GameStarted from "../events/game-event/game-started.js"; +import * as PauseStateChanged from "../events/game-event/pause-state-changed.js"; +import * as PlaySubmitted from "../events/game-event/play-submitted.js"; +import * as PlayingStarted from "../events/game-event/playing-started.js"; +import * as RoundStarted from "../events/game-event/round-started.js"; +import type { Lobby } from "../lobby.js"; +import type { ServerState } from "../server-state.js"; +import type * as Timeout from "../timeout.js"; +import * as FinishedPlaying from "../timeout/finished-playing.js"; +import * as RoundStageTimerDone from "../timeout/round-stage-timer-done.js"; +import type * as User from "../user.js"; +import * as Util from "../util.js"; +import * as Card from "./cards/card.js"; +import * as Decks from "./cards/decks.js"; +import * as Play from "./cards/play.js"; +import * as Round from "./game/round.js"; +import type * as PublicRound from "./game/round/public.js"; +import type { StoredPlay } from "./game/round/stored-play.js"; +import * as Player from "./player.js"; +import * as Rules from "./rules.js"; +import * as HappyEnding from "./rules/happy-ending.js"; export interface Public { round: PublicRound.Public; @@ -107,7 +107,13 @@ export class Game { ); } - private static canBeCzar(user: User.User, player?: Player.Player): boolean { + private static canBeCzar( + user: User.User | undefined, + player?: Player.Player | undefined, + ): boolean { + if (user === undefined) { + return false; + } return user.control !== "Computer" && Game.activePlayer(user, player); } @@ -142,14 +148,13 @@ export class Game { nextIndex += 1; nextIndex = nextIndex >= playerOrder.length ? 0 : nextIndex; } - let triedEveryone = false; incrementIndex(); while (!triedEveryone) { if (nextIndex === currentIndex) { triedEveryone = true; } - const potentialCzar = playerOrder[nextIndex]; + const potentialCzar = playerOrder[nextIndex] as string; if (Game.canBeCzar(users[potentialCzar], players[potentialCzar])) { return potentialCzar; } @@ -208,7 +213,7 @@ export class Game { } const playersInRound = new Set( wu(playerOrder).filter((id) => - Game.isPlayerInRound(czar, playerMap, id, users[id]), + Game.isPlayerInRound(czar, playerMap, id, users[id] as User.User), ), ); let round: Round.Starting | Round.Playing; @@ -292,7 +297,12 @@ export class Game { const roundId = round.id + 1; const playersInRound = new Set( wu(this.playerOrder).filter((id) => - Game.isPlayerInRound(czar, this.players, id, lobby.users[id]), + Game.isPlayerInRound( + czar, + this.players, + id, + lobby.users[id] as User.User, + ), ), ); if (this.rules.houseRules.happyEnding?.inFinalRound) { @@ -317,7 +327,7 @@ export class Game { ); } } - let atStart = this.startRound(server, false, this.round); + const atStart = this.startRound(server, false, this.round); return { events: [ ...events, @@ -334,7 +344,7 @@ export class Game { */ public removeFromRound( toRemove: User.Id, - server: ServerState, + _server: ServerState, ): { timeouts?: Iterable } { const player = this.players[toRemove]; if (player !== undefined && this.round.stage !== "Starting") { diff --git a/server/src/ts/games/game/round.ts b/server/src/ts/games/game/round.ts index 9f7281a0..7740ccd6 100644 --- a/server/src/ts/games/game/round.ts +++ b/server/src/ts/games/game/round.ts @@ -1,24 +1,25 @@ import wu from "wu"; -import { Action } from "../../action"; -import { IncorrectRoundStageError } from "../../errors/action-execution-error"; -import * as User from "../../user"; -import * as Util from "../../util"; -import * as Card from "../cards/card"; -import * as Play from "../cards/play"; -import * as PublicRound from "./round/public"; -import * as StoredPlay from "./round/stored-play"; -import * as RoundStageTimerDone from "../../timeout/round-stage-timer-done"; -import * as Timeout from "../../timeout"; -import * as Event from "../../event"; -import * as StartJudging from "../../events/game-event/start-judging"; -import * as StartRevealing from "../../events/game-event/start-revealing"; -import * as Rules from "../rules"; -import * as Game from "../game"; -import { InvalidActionError } from "../../errors/validation"; -import { ServerState } from "../../server-state"; -import { Part } from "../cards/card"; -import * as CzarChoices from "../rules/czar-choices"; -import { Decks } from "../cards/decks"; + +import type { Action } from "../../action.js"; +import { IncorrectRoundStageError } from "../../errors/action-execution-error.js"; +import { InvalidActionError } from "../../errors/validation.js"; +import * as Event from "../../event.js"; +import * as StartJudging from "../../events/game-event/start-judging.js"; +import * as StartRevealing from "../../events/game-event/start-revealing.js"; +import type { ServerState } from "../../server-state.js"; +import type * as Timeout from "../../timeout.js"; +import * as RoundStageTimerDone from "../../timeout/round-stage-timer-done.js"; +import type * as User from "../../user.js"; +import * as Util from "../../util.js"; +import type { Part } from "../cards/card.js"; +import * as Card from "../cards/card.js"; +import type { Decks } from "../cards/decks.js"; +import type * as Play from "../cards/play.js"; +import type * as Game from "../game.js"; +import type * as Rules from "../rules.js"; +import * as CzarChoices from "../rules/czar-choices.js"; +import type * as PublicRound from "./round/public.js"; +import * as StoredPlay from "./round/stored-play.js"; export type Round = Starting | Playing | Revealing | Judging | Complete; @@ -78,7 +79,7 @@ export interface Timed { export const isTimed = ( round: Base, -): round is Base & Timed => round.hasOwnProperty("timedOut"); +): round is Base & Timed => Object.hasOwn(round, "timedOut"); export class Complete extends Base<"Complete"> { public get stage(): "Complete" { diff --git a/server/src/ts/games/game/round/public.ts b/server/src/ts/games/game/round/public.ts index d9514ef3..bc7abce3 100644 --- a/server/src/ts/games/game/round/public.ts +++ b/server/src/ts/games/game/round/public.ts @@ -1,7 +1,7 @@ -import * as User from "../../../user"; -import * as Card from "../../cards/card"; -import * as Play from "../../cards/play"; -import * as Player from "../../player"; +import type * as User from "../../../user.js"; +import type * as Card from "../../cards/card.js"; +import type * as Play from "../../cards/play.js"; +import type * as Player from "../../player.js"; export type Public = Starting | Playing | Revealing | Judging | Complete; diff --git a/server/src/ts/games/game/round/stored-play.ts b/server/src/ts/games/game/round/stored-play.ts index 13181abb..32eabedb 100644 --- a/server/src/ts/games/game/round/stored-play.ts +++ b/server/src/ts/games/game/round/stored-play.ts @@ -1,6 +1,6 @@ -import * as User from "../../../user"; -import * as Play from "../../cards/play"; -import { Round } from "../round"; +import type * as User from "../../../user.js"; +import type * as Play from "../../cards/play.js"; +import type { Round } from "../round.js"; export interface StoredPlay { id: Play.Id; @@ -22,7 +22,7 @@ const isRevealed = (play: StoredPlay): play is Revealed => play.revealed; * Checks if every stored play in a round is revealed or not. */ export const allRevealed = ( - round: TRound & { plays: StoredPlay[] } + round: TRound & { plays: StoredPlay[] }, ): round is TRound & { plays: Revealed[]; } => round.plays.every(isRevealed); diff --git a/server/src/ts/games/player.ts b/server/src/ts/games/player.ts index ae4b6eca..b7fd69a2 100644 --- a/server/src/ts/games/player.ts +++ b/server/src/ts/games/player.ts @@ -1,6 +1,6 @@ -import * as User from "../user"; -import { Hand } from "./cards/hand"; -import { Game } from "./game"; +import type * as User from "../user.js"; +import type { Hand } from "./cards/hand.js"; +import type { Game } from "./game.js"; /** * A player containing only state all users can see. diff --git a/server/src/ts/games/rules.ts b/server/src/ts/games/rules.ts index eca6f840..ccd104e9 100644 --- a/server/src/ts/games/rules.ts +++ b/server/src/ts/games/rules.ts @@ -1,5 +1,5 @@ -import * as HouseRules from "./rules/house-rules"; -import * as Rando from "./rules/rando"; +import * as HouseRules from "./rules/house-rules.js"; +import * as Rando from "./rules/rando.js"; /** The rules for a standard game. */ diff --git a/server/src/ts/games/rules/czar-choices.ts b/server/src/ts/games/rules/czar-choices.ts index 40ca5fba..dfc1b373 100644 --- a/server/src/ts/games/rules/czar-choices.ts +++ b/server/src/ts/games/rules/czar-choices.ts @@ -1,4 +1,4 @@ -import * as Card from "../cards/card"; +import * as Card from "../cards/card.js"; /** * Configuration for the "Czar Choices" house rule. diff --git a/server/src/ts/games/rules/happy-ending.ts b/server/src/ts/games/rules/happy-ending.ts index 8463e2c2..e8b9e5bb 100644 --- a/server/src/ts/games/rules/happy-ending.ts +++ b/server/src/ts/games/rules/happy-ending.ts @@ -1,4 +1,4 @@ -import * as Card from "../cards/card"; +import * as Card from "../cards/card.js"; /** * Configuration for the "Happy Ending" house rule. diff --git a/server/src/ts/games/rules/house-rules.ts b/server/src/ts/games/rules/house-rules.ts index b6540eb9..28de410c 100644 --- a/server/src/ts/games/rules/house-rules.ts +++ b/server/src/ts/games/rules/house-rules.ts @@ -1,8 +1,13 @@ -import { PackingHeat, Reboot, ComedyWriter, NeverHaveIEver } from "../rules"; -import * as Rando from "./rando"; -import * as HappyEnding from "./happy-ending"; -import * as CzarChoices from "./czar-choices"; -import * as WinnersPick from "./winners-pick"; +import type { + ComedyWriter, + NeverHaveIEver, + PackingHeat, + Reboot, +} from "../rules.js"; +import type * as CzarChoices from "./czar-choices.js"; +import type * as HappyEnding from "./happy-ending.js"; +import * as Rando from "./rando.js"; +import type * as WinnersPick from "./winners-pick.js"; /** * Non-standard rules that can be applied to a game. diff --git a/server/src/ts/games/rules/rando.ts b/server/src/ts/games/rules/rando.ts index eacbfd18..552af8b5 100644 --- a/server/src/ts/games/rules/rando.ts +++ b/server/src/ts/games/rules/rando.ts @@ -1,9 +1,9 @@ -import { RegisterUser } from "../../action/initial/register-user"; -import * as Event from "../../event"; -import * as PresenceChanged from "../../events/lobby-event/presence-changed"; -import * as Lobby from "../../lobby"; -import * as User from "../../user"; -import * as Util from "../../util"; +import type { RegisterUser } from "../../action/initial/register-user.js"; +import * as Event from "../../event.js"; +import * as PresenceChanged from "../../events/lobby-event/presence-changed.js"; +import * as Lobby from "../../lobby.js"; +import type * as User from "../../user.js"; +import * as Util from "../../util.js"; /** * The maximum number of AI players allowed in a single game. @@ -76,12 +76,16 @@ const isId = (ai: User.Id | RegisterUser): ai is User.Id => export const createIfNeeded = ( inLobby: Lobby.Lobby, - ai: User.Id | RegisterUser + ai: User.Id | RegisterUser, ): { user: User.Id; events: Iterable } => { if (isId(ai)) { return { user: ai, - events: [Event.targetAll(PresenceChanged.joined(ai, inLobby.users[ai]))], + events: [ + Event.targetAll( + PresenceChanged.joined(ai, inLobby.users[ai] as User.User), + ), + ], }; } else { return Lobby.addUser(inLobby, ai, "Player", (user) => ({ @@ -94,13 +98,13 @@ export const createIfNeeded = ( function* add( inLobby: Lobby.Lobby, config: Rando, - number: number + number: number, ): Iterable { const added = config.unused .splice(0, number) .map((ai) => createIfNeeded(inLobby, ai)); for (const { user, events } of added) { - const userData = inLobby.users[user]; + const userData = inLobby.users[user] as User.User; userData.presence = "Joined"; config.current.push(user); yield* events; @@ -134,7 +138,7 @@ export const create = (inLobby: Lobby.Lobby, initial?: Public): Rando => { export function* change( inLobby: Lobby.Lobby, config: Rando, - changeTo?: Public + changeTo?: Public, ): Iterable { const want = changeTo !== undefined ? changeTo.number : 0; const have = config.current.length; @@ -147,10 +151,10 @@ export function* change( const toRemove = have - want; const removed = config.current.splice( config.current.length - toRemove, - toRemove + toRemove, ); for (const ai of removed) { - const user = inLobby.users[ai]; + const user = inLobby.users[ai] as User.User; user.presence = "Left"; yield Event.targetAll(PresenceChanged.left(ai, "Left")); } diff --git a/server/src/ts/index.ts b/server/src/ts/index.ts index e031d4ed..4542dae8 100644 --- a/server/src/ts/index.ts +++ b/server/src/ts/index.ts @@ -1,6 +1,7 @@ +import "express-async-errors"; + import bodyParser from "body-parser"; import express, { NextFunction, Request, Response } from "express"; -import "express-async-errors"; import expressWinston from "express-winston"; import ws from "express-ws"; import { promises as fs } from "fs"; @@ -9,24 +10,25 @@ import HttpStatus from "http-status-codes"; import JSON5 from "json5"; import sourceMapSupport from "source-map-support"; import wu from "wu"; -import * as CheckAlive from "./action/initial/check-alive"; -import * as CreateLobby from "./action/initial/create-lobby"; -import * as RegisterUser from "./action/initial/register-user"; -import * as ServerConfig from "./config"; -import { MassiveDecksError } from "./errors"; -import { InvalidLobbyPasswordError } from "./errors/authentication"; -import { UsernameAlreadyInUseError } from "./errors/registration"; -import * as Event from "./event"; -import * as PresenceChanged from "./events/lobby-event/presence-changed"; -import * as Player from "./games/player"; -import * as Change from "./lobby/change"; -import { GameCode } from "./lobby/game-code"; -import * as Logging from "./logging"; -import * as ServerState from "./server-state"; -import * as Timeout from "./timeout"; -import * as UserDisconnect from "./timeout/user-disconnect"; -import * as User from "./user"; -import * as Token from "./user/token"; + +import * as CheckAlive from "./action/initial/check-alive.js"; +import * as CreateLobby from "./action/initial/create-lobby.js"; +import * as RegisterUser from "./action/initial/register-user.js"; +import * as ServerConfig from "./config.js"; +import { MassiveDecksError } from "./errors.js"; +import { InvalidLobbyPasswordError } from "./errors/authentication.js"; +import { UsernameAlreadyInUseError } from "./errors/registration.js"; +import * as Event from "./event.js"; +import * as PresenceChanged from "./events/lobby-event/presence-changed.js"; +import * as Player from "./games/player.js"; +import * as Change from "./lobby/change.js"; +import type { GameCode } from "./lobby/game-code.js"; +import * as Logging from "./logging.js"; +import * as ServerState from "./server-state.js"; +import * as Timeout from "./timeout.js"; +import * as UserDisconnect from "./timeout/user-disconnect.js"; +import * as User from "./user.js"; +import * as Token from "./user/token.js"; sourceMapSupport.install(); @@ -192,7 +194,7 @@ async function main(): Promise { }); app.ws("/api/games/:gameCode", async (socket, req) => { - const gameCode = req.params.gameCode; + const gameCode = req.params["gameCode"] as string; state.socketManager.add(state, gameCode, socket); }); diff --git a/server/src/ts/lobby.ts b/server/src/ts/lobby.ts index 5c9c59c5..33b22f77 100644 --- a/server/src/ts/lobby.ts +++ b/server/src/ts/lobby.ts @@ -1,16 +1,16 @@ -import { CreateLobby } from "./action/initial/create-lobby"; -import { RegisterUser } from "./action/initial/register-user"; -import * as Errors from "./errors"; -import * as Event from "./event"; -import * as PresenceChanged from "./events/lobby-event/presence-changed"; -import * as Game from "./games/game"; -import * as Rules from "./games/rules"; -import * as Config from "./lobby/config"; -import { GameCode } from "./lobby/game-code"; -import * as User from "./user"; -import * as Util from "./util"; -import { LoadDeckSummary } from "./task/load-deck-summary"; -import * as Rando from "./games/rules/rando"; +import type { CreateLobby } from "./action/initial/create-lobby.js"; +import type { RegisterUser } from "./action/initial/register-user.js"; +import type * as Errors from "./errors.js"; +import * as Event from "./event.js"; +import * as PresenceChanged from "./events/lobby-event/presence-changed.js"; +import * as Game from "./games/game.js"; +import * as Rules from "./games/rules.js"; +import * as Rando from "./games/rules/rando.js"; +import * as Config from "./lobby/config.js"; +import type { GameCode } from "./lobby/game-code.js"; +import { LoadDeckSummary } from "./task/load-deck-summary.js"; +import * as User from "./user.js"; +import * as Util from "./util.js"; /** * A game lobby. @@ -77,13 +77,13 @@ export interface Summary { export const fromDefaults = ( gameCode: GameCode, name: string, - defaults: Config.Defaults + defaults: Config.Defaults, ): { config: Config.Config; tasks: LoadDeckSummary[]; } => { const tasks = defaults.decks.map( - (source) => new LoadDeckSummary(gameCode, source) + (source) => new LoadDeckSummary(gameCode, source), ); return { config: { @@ -109,7 +109,7 @@ export const fromDefaults = ( export function create( gameCode: GameCode, creation: CreateLobby, - defaults: Config.Defaults + defaults: Config.Defaults, ): { lobby: Lobby; tasks: LoadDeckSummary[]; @@ -125,7 +125,7 @@ export function create( }; config.rules.houseRules.rando = Rando.create( lobby, - defaults.rules.houseRules.rando + defaults.rules.houseRules.rando, ); return { lobby, @@ -175,7 +175,7 @@ export const addUser = ( lobby: Lobby, registration: RegisterUser, role: User.Role, - change?: (user: User.User) => User.User + change?: (user: User.User) => User.User, ): { user: User.Id; events: Iterable; diff --git a/server/src/ts/lobby/change.ts b/server/src/ts/lobby/change.ts index 7b5b5616..aba0fd6f 100644 --- a/server/src/ts/lobby/change.ts +++ b/server/src/ts/lobby/change.ts @@ -1,11 +1,11 @@ -import { GameStateError } from "../errors/game-state-error"; -import * as Event from "../event"; -import * as ErrorEncountered from "../events/lobby-event/error-encountered"; -import { Lobby } from "../lobby"; -import { ServerState } from "../server-state"; -import { Task } from "../task"; -import * as Timeout from "../timeout"; -import { GameCode } from "./game-code"; +import { GameStateError } from "../errors/game-state-error.js"; +import * as Event from "../event.js"; +import * as ErrorEncountered from "../events/lobby-event/error-encountered.js"; +import type { Lobby } from "../lobby.js"; +import type { ServerState } from "../server-state.js"; +import type { Task } from "../task.js"; +import * as Timeout from "../timeout.js"; +import type { GameCode } from "./game-code.js"; export interface Change { lobby?: Lobby; @@ -17,9 +17,10 @@ export interface Change { export type ConstrainedChange = Change & { lobby?: L }; export type Handler = (lobby: Lobby) => Change; -export type HandlerWithReturnValue = ( - lobby: Lobby -) => { change: Change; returnValue: T }; +export type HandlerWithReturnValue = (lobby: Lobby) => { + change: Change; + returnValue: T; +}; /** * Reduce a list of items to a single change by applying a function to each one @@ -31,7 +32,7 @@ export type HandlerWithReturnValue = ( export function reduce( items: Iterable, lobby: L, - toChange: (lobby: L, item: T) => ConstrainedChange + toChange: (lobby: L, item: T) => ConstrainedChange, ): ConstrainedChange { let currentLobby = lobby; let lobbyChanged = false; @@ -66,7 +67,7 @@ function internalApply( server: ServerState, gameCode: GameCode, originalLobby: Lobby, - change: Change + change: Change, ): { lobby?: Lobby; timeouts?: Iterable; @@ -87,7 +88,7 @@ function internalApply( server, gameCode, currentLobby, - Timeout.handler(server, timeoutAfter.timeout, gameCode, currentLobby) + Timeout.handler(server, timeoutAfter.timeout, gameCode, currentLobby), ); if (chained.lobby !== undefined) { lobbyUnchanged = false; @@ -125,7 +126,7 @@ export async function applyAndReturn( server: ServerState, gameCode: GameCode, handler: HandlerWithReturnValue, - timeoutId?: Timeout.Id + timeoutId?: Timeout.Id, ): Promise { try { const [tasks, returnValue] = await server.store.writeAndReturn( @@ -138,7 +139,7 @@ export async function applyAndReturn( server.socketManager, gameCode, result.lobby ?? lobby, - result.events + result.events, ); } return { @@ -149,7 +150,7 @@ export async function applyAndReturn( }, result: [result.tasks, returnValue], }; - } + }, ); if (tasks !== undefined) { for (const task of tasks) { @@ -184,7 +185,7 @@ export async function apply( server: ServerState, gameCode: GameCode, handler: Handler, - timeoutId?: Timeout.Id + timeoutId?: Timeout.Id, ): Promise { await applyAndReturn( server, @@ -193,6 +194,6 @@ export async function apply( change: handler(lobby), returnValue: undefined, }), - timeoutId + timeoutId, ); } diff --git a/server/src/ts/lobby/config.ts b/server/src/ts/lobby/config.ts index f03c543f..b3640d61 100644 --- a/server/src/ts/lobby/config.ts +++ b/server/src/ts/lobby/config.ts @@ -1,5 +1,5 @@ -import * as Source from "../games/cards/source"; -import * as Rules from "../games/rules"; +import type * as Source from "../games/cards/source.js"; +import * as Rules from "../games/rules.js"; /** * Configuration for a lobby. @@ -72,7 +72,7 @@ export interface FailedSource { } export const isFailed = (source: ConfiguredSource): source is FailedSource => - source.hasOwnProperty("failure"); + Object.hasOwn(source, "failure"); export const censor = (config: Config): Public => ({ version: config.version.toString(), diff --git a/server/src/ts/lobby/game-code.ts b/server/src/ts/lobby/game-code.ts index 7c23d454..388cf588 100644 --- a/server/src/ts/lobby/game-code.ts +++ b/server/src/ts/lobby/game-code.ts @@ -18,7 +18,7 @@ export type LobbyId = number; const hashIds = new Hashids( "massivedecks", 2, - "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", ); /** diff --git a/server/src/ts/logging.ts b/server/src/ts/logging.ts index 053eca5b..fa54c119 100644 --- a/server/src/ts/logging.ts +++ b/server/src/ts/logging.ts @@ -9,7 +9,7 @@ const logFormat = winston.format.printf((info) => { splat: undefined, }, null, - 2 + 2, ); if (stringRest !== "{}") { @@ -24,19 +24,18 @@ export const logger = winston.createLogger({ format: winston.format.combine( winston.format.timestamp(), winston.format.colorize(), - logFormat + logFormat, ), }); const exceptionHandler = new winston.ExceptionHandler(logger); -export const exceptionToMeta = exceptionHandler.getAllInfo.bind( - exceptionHandler -); +export const exceptionToMeta = + exceptionHandler.getAllInfo.bind(exceptionHandler); export const logException = ( message: string, error: Error, - data?: string + data?: string, ): void => { const details = exceptionToMeta(error); logger.error(message, details === undefined ? details : { details, data }); diff --git a/server/src/ts/server-state.ts b/server/src/ts/server-state.ts index 801e5cf2..e7ab9d81 100644 --- a/server/src/ts/server-state.ts +++ b/server/src/ts/server-state.ts @@ -1,11 +1,11 @@ -import { Cache } from "./cache"; -import * as caches from "./caches"; -import * as Config from "./config"; -import { SocketManager } from "./socket-manager"; -import { Store } from "./store"; -import * as Stores from "./store/stores"; -import * as Tasks from "./task/tasks"; -import { Sources } from "./games/cards/sources"; +import type { Cache } from "./cache.js"; +import * as caches from "./caches.js"; +import type * as Config from "./config.js"; +import { Sources } from "./games/cards/sources.js"; +import { SocketManager } from "./socket-manager.js"; +import type { Store } from "./store.js"; +import * as Stores from "./store/stores.js"; +import * as Tasks from "./task/tasks.js"; export interface ServerState { config: Config.Parsed; diff --git a/server/src/ts/socket-manager.ts b/server/src/ts/socket-manager.ts index 1ab60292..e8ae9f2a 100644 --- a/server/src/ts/socket-manager.ts +++ b/server/src/ts/socket-manager.ts @@ -1,19 +1,20 @@ -import WebSocket from "ws"; -import * as Action from "./action"; -import * as Authenticate from "./action/authenticate"; -import { MassiveDecksError } from "./errors"; -import { NotAuthenticatedError } from "./errors/authentication"; -import { InvalidActionError } from "./errors/validation"; -import * as Event from "./event"; -import * as Sync from "./events/user-event/sync"; -import * as Lobby from "./lobby"; -import * as Change from "./lobby/change"; -import { GameCode } from "./lobby/game-code"; -import * as Logging from "./logging"; -import { ServerState } from "./server-state"; -import * as UserDisconnect from "./timeout/user-disconnect"; -import * as User from "./user"; -import * as Token from "./user/token"; +import type WebSocket from "ws"; + +import * as Action from "./action.js"; +import * as Authenticate from "./action/authenticate.js"; +import { MassiveDecksError } from "./errors.js"; +import { NotAuthenticatedError } from "./errors/authentication.js"; +import { InvalidActionError } from "./errors/validation.js"; +import * as Event from "./event.js"; +import * as Sync from "./events/user-event/sync.js"; +import * as Lobby from "./lobby.js"; +import * as Change from "./lobby/change.js"; +import type { GameCode } from "./lobby/game-code.js"; +import * as Logging from "./logging.js"; +import type { ServerState } from "./server-state.js"; +import * as UserDisconnect from "./timeout/user-disconnect.js"; +import type * as User from "./user.js"; +import type * as Token from "./user/token.js"; const parseJson = (raw: string): object => { try { @@ -95,7 +96,7 @@ export class SocketManager { private readonly errorWSHandler = ( socket: WebSocket, - fn: (data: WebSocket.Data) => Promise + fn: (data: WebSocket.Data) => Promise, ): ((data: WebSocket.Data) => Promise) => async (data) => { try { @@ -157,7 +158,7 @@ export class SocketManager { .filter((p) => p.likes.some((l) => l === uid)) .map((p) => p.id); const playedCard = round.plays.find( - (p) => p.playedBy === uid + (p) => p.playedBy === uid, ); const played = playedCard === undefined ? undefined : playedCard.id; @@ -167,7 +168,7 @@ export class SocketManager { calls = round.czar === uid ? round.calls : undefined; } else { const potentialPlay = round.plays.find( - (play) => play.playedBy === uid + (play) => play.playedBy === uid, ); if (potentialPlay !== undefined) { play = potentialPlay.play.map((card) => card.id); @@ -175,14 +176,14 @@ export class SocketManager { } } - const user = lobby.users[uid]; + const user = lobby.users[uid] as User.User; user.connection = "Connected"; return { lobby, events: [ Event.targetOnly( Sync.of(Lobby.censor(lobby), hand, play, likeDetail, calls), - uid + uid, ), ], }; @@ -197,14 +198,14 @@ export class SocketManager { } else { const claims = auth; await Change.apply(server, auth.gc, (lobby) => - Action.handle(claims, lobby, validated, server) + Action.handle(claims, lobby, validated, server), ); Logging.logger.info("WebSocket receive:", { user: auth.uid, action: validated, }); } - }) + }), ); socket.on( "close", @@ -225,7 +226,7 @@ export class SocketManager { Logging.logger.info("User disconnect:", { user: auth.uid }); } } - }) + }), ); } } diff --git a/server/src/ts/store.ts b/server/src/ts/store.ts index 8b434320..e4a827a1 100644 --- a/server/src/ts/store.ts +++ b/server/src/ts/store.ts @@ -1,11 +1,11 @@ -import { CreateLobby } from "./action/initial/create-lobby"; -import * as ServerConfig from "./config"; -import * as Lobby from "./lobby"; -import { GameCode } from "./lobby/game-code"; -import * as Timeout from "./timeout"; -import { Token } from "./user/token"; -import { Task } from "./task"; -import * as LobbyConfig from "./lobby/config"; +import type { CreateLobby } from "./action/initial/create-lobby.js"; +import type * as ServerConfig from "./config.js"; +import type * as Lobby from "./lobby.js"; +import type * as LobbyConfig from "./lobby/config.js"; +import type { GameCode } from "./lobby/game-code.js"; +import type { Task } from "./task.js"; +import type * as Timeout from "./timeout.js"; +import type { Token } from "./user/token.js"; /** * Represents a chunk of data that should be written as a single transaction, @@ -45,20 +45,20 @@ export abstract class Store { * invalid, but if there is any security concern, change the application * secret, not this. That will have the same effect securely. */ - public abstract id(): Promise; + public abstract id(): Promise; /** * Returns if the given lobby exists. */ - public abstract exists(gameCode: GameCode): Promise; + public abstract exists(gameCode: GameCode): Promise; /** Create a new lobby. * @return The game code for the new lobby and the user id for the owner. */ - public abstract newLobby( + public abstract newLobby( creation: CreateLobby, secret: string, - defaults: LobbyConfig.Defaults + defaults: LobbyConfig.Defaults, ): Promise<{ gameCode: GameCode; token: Token; tasks: Iterable }>; /** @@ -68,9 +68,10 @@ export abstract class Store { */ public async read( gameCode: GameCode, - read: ( - lobby: Lobby.Lobby - ) => { transaction: ReadOnlyTransaction; result: T } + read: (lobby: Lobby.Lobby) => { + transaction: ReadOnlyTransaction; + result: T; + }, ): Promise { return this.writeAndReturn(gameCode, read); } @@ -83,7 +84,7 @@ export abstract class Store { */ public async write( gameCode: GameCode, - write: (lobby: Lobby.Lobby) => Transaction + write: (lobby: Lobby.Lobby) => Transaction, ): Promise { await this.writeAndReturn(gameCode, (lobby: Lobby.Lobby) => ({ transaction: write(lobby), @@ -91,9 +92,9 @@ export abstract class Store { })); } - public abstract writeAndReturn( + public abstract writeAndReturn( gameCode: GameCode, - write: (lobby: Lobby.Lobby) => { transaction: Transaction; result: T } + write: (lobby: Lobby.Lobby) => { transaction: Transaction; result: T }, ): Promise; /** Get a list of summaries for all the public lobbies in the store.*/ @@ -107,12 +108,12 @@ export abstract class Store { /** * Delete the given lobby and all associated timeouts. */ - public abstract delete(gameCode: GameCode): Promise; + public abstract delete(gameCode: GameCode): Promise; /** * Remove lobbies where the game is finished or everyone has been * disconnected for some time. * This should also clean up the cache as appropriate. */ - public abstract garbageCollect(): Promise; + public abstract garbageCollect(): Promise; } diff --git a/server/src/ts/store/in-memory.ts b/server/src/ts/store/in-memory.ts index 90af7719..16451d07 100644 --- a/server/src/ts/store/in-memory.ts +++ b/server/src/ts/store/in-memory.ts @@ -1,24 +1,16 @@ import * as uuid from "uuid"; import wu from "wu"; -import { CreateLobby } from "../action/initial/create-lobby"; -import * as ServerConfig from "../config"; -import { LobbyClosedError, LobbyDoesNotExistError } from "../errors/lobby"; -import * as Lobby from "../lobby"; -import * as GameCode from "../lobby/game-code"; -import { Store, Transaction } from "../store"; -import * as Timeout from "../timeout"; -import * as Token from "../user/token"; -import * as LobbyConfig from "../lobby/config"; -import { Task } from "../task"; -declare module "wu" { - // Fix incorrect types. - // noinspection JSUnusedGlobalSymbols - interface WuIterable { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - spreadMap(fn: (...x: any[]) => U): WuIterable; - } -} +import type { CreateLobby } from "../action/initial/create-lobby.js"; +import type * as ServerConfig from "../config.js"; +import { LobbyClosedError, LobbyDoesNotExistError } from "../errors/lobby.js"; +import * as Lobby from "../lobby.js"; +import type * as LobbyConfig from "../lobby/config.js"; +import * as GameCode from "../lobby/game-code.js"; +import { Store, Transaction } from "../store.js"; +import type { Task } from "../task.js"; +import type * as Timeout from "../timeout.js"; +import * as Token from "../user/token.js"; interface TimeoutMeta { timeout: Timeout.Timeout; @@ -68,7 +60,7 @@ export class InMemoryStore extends Store { public async newLobby( creation: CreateLobby, secret: string, - defaults: LobbyConfig.Defaults + defaults: LobbyConfig.Defaults, ): Promise<{ gameCode: GameCode.GameCode; token: Token.Token; @@ -86,7 +78,7 @@ export class InMemoryStore extends Store { uid: lobby.owner, }, this._id, - secret + secret, ), tasks, }; @@ -108,7 +100,7 @@ export class InMemoryStore extends Store { public async writeAndReturn( gameCode: GameCode.GameCode, - write: (lobby: Lobby.Lobby) => { transaction: Transaction; result: T } + write: (lobby: Lobby.Lobby) => { transaction: Transaction; result: T }, ): Promise { const { transaction, result } = write(await this.lobby(gameCode)); if (transaction.lobby !== undefined) { @@ -138,7 +130,7 @@ export class InMemoryStore extends Store { if ( lastWrite + this.config.abandonedTime < Date.now() || wu(Object.values(lobby.users)).every( - (u) => u.control === "Computer" || u.presence === "Left" + (u) => u.control === "Computer" || u.presence === "Left", ) ) { toRemove.add(gameCode); diff --git a/server/src/ts/store/postgres.ts b/server/src/ts/store/postgres.ts index 9d333691..6c14d46f 100644 --- a/server/src/ts/store/postgres.ts +++ b/server/src/ts/store/postgres.ts @@ -1,17 +1,18 @@ -import Pg from "pg"; +import type Pg from "pg"; import * as uuid from "uuid"; -import { CreateLobby } from "../action/initial/create-lobby"; -import * as Config from "../config"; -import { LobbyClosedError } from "../errors/lobby"; -import * as Lobby from "../lobby"; -import * as GameCode from "../lobby/game-code"; -import * as Store from "../store"; -import * as Timeout from "../timeout"; -import * as Token from "../user/token"; -import * as Postgres from "../util/postgres"; -import * as LobbyConfig from "../lobby/config"; -import { LoadDeckSummary } from "../task/load-deck-summary"; -import * as Task from "../task"; + +import type { CreateLobby } from "../action/initial/create-lobby.js"; +import type * as Config from "../config.js"; +import { LobbyClosedError } from "../errors/lobby.js"; +import * as Lobby from "../lobby.js"; +import type * as LobbyConfig from "../lobby/config.js"; +import * as GameCode from "../lobby/game-code.js"; +import * as Store from "../store.js"; +import type * as Task from "../task.js"; +import { LoadDeckSummary } from "../task/load-deck-summary.js"; +import type * as Timeout from "../timeout.js"; +import * as Token from "../user/token.js"; +import * as Postgres from "../util/postgres.js"; class To0 extends Postgres.Upgrade { public readonly to = 0; @@ -19,7 +20,7 @@ class To0 extends Postgres.Upgrade { public async apply(client: Pg.PoolClient): Promise<0> { await client.query("CREATE SCHEMA massivedecks;"); await client.query( - "CREATE TABLE massivedecks.meta ( version INTEGER NOT NULL, id UUID NOT NULL );" + "CREATE TABLE massivedecks.meta ( version INTEGER NOT NULL, id UUID NOT NULL );", ); await client.query(` CREATE TABLE massivedecks.lobbies ( @@ -81,12 +82,12 @@ export class PostgresStore extends Store.Store { private readonly pg: Postgres.Postgres; public static async create( - config: Config.PostgreSQL + config: Config.PostgreSQL, ): Promise { const pg = new Postgres.Postgres( "massivedecks", config.connection, - upgrades + upgrades, ); await pg.ensureCurrent(); return await pg.withClient(async (client) => { @@ -98,7 +99,7 @@ export class PostgresStore extends Store.Store { private constructor( id: string, config: Config.PostgreSQL, - pg: Postgres.Postgres + pg: Postgres.Postgres, ) { super(); this.cachedId = id; @@ -117,9 +118,9 @@ export class PostgresStore extends Store.Store { ( await client.query( "SELECT EXISTS (SELECT id FROM massivedecks.lobbies WHERE id = $1)", - [lobbyId] + [lobbyId], ) - ).rows[0].exists + ).rows[0].exists, ); } @@ -129,7 +130,7 @@ export class PostgresStore extends Store.Store { async (client) => await client.query("DELETE FROM massivedecks.lobbies WHERE id = $1", [ lobbyId, - ]) + ]), ); } @@ -141,7 +142,7 @@ export class PostgresStore extends Store.Store { ((last_access + $1::interval ) < NOW()) OR (SELECT bool_and(value->>'control' = 'Computer' OR value->>'presence' = 'Left') FROM jsonb_each(lobby->'users')); `, - [`${this.config.abandonedTime} milliseconds`] + [`${this.config.abandonedTime} milliseconds`], ); return result.rowCount; }); @@ -149,12 +150,12 @@ export class PostgresStore extends Store.Store { public async *lobbySummaries(): AsyncIterableIterator { yield* this.pg.withClientIterator( - PostgresStore.lobbySummariesInternal.bind(this) + PostgresStore.lobbySummariesInternal.bind(this), ); } private static async *lobbySummariesInternal( - client: Pg.PoolClient + client: Pg.PoolClient, ): AsyncIterableIterator { const result = await client.query("SELECT * FROM massivedecks.summaries"); for (const { id, name, started, ended, users, password } of result.rows) { @@ -174,7 +175,7 @@ export class PostgresStore extends Store.Store { public async newLobby( creation: CreateLobby, secret: string, - defaults: LobbyConfig.Defaults + defaults: LobbyConfig.Defaults, ): Promise<{ gameCode: GameCode.GameCode; token: Token.Token; @@ -184,11 +185,11 @@ export class PostgresStore extends Store.Store { const { lobby, tasks } = Lobby.create( "fake-game-code", creation, - defaults + defaults, ); const result = await client.query( "INSERT INTO massivedecks.lobbies VALUES (DEFAULT, $1) RETURNING id", - [lobby] + [lobby], ); const gameCode = GameCode.encode(result.rows[0]["id"]); return { @@ -199,7 +200,7 @@ export class PostgresStore extends Store.Store { uid: lobby.owner, }, await this.id(), - secret + secret, ), tasks: tasks.map((task) => new LoadDeckSummary(gameCode, task.source)), }; @@ -211,11 +212,11 @@ export class PostgresStore extends Store.Store { } private static async *timedOutInternal( - client: Pg.PoolClient + client: Pg.PoolClient, ): AsyncIterableIterator { const result = await client.query( "SELECT * FROM massivedecks.timeouts WHERE after < $1;", - [Date.now()] + [Date.now()], ); for (const row of result.rows) { yield { @@ -228,31 +229,34 @@ export class PostgresStore extends Store.Store { public async writeAndReturn( gameCode: string, - write: (lobby: Lobby.Lobby) => { transaction: Store.Transaction; result: T } + write: (lobby: Lobby.Lobby) => { + transaction: Store.Transaction; + result: T; + }, ): Promise { const lobbyId = GameCode.decode(gameCode); return await this.pg.inTransaction(async (client) => { const get = await client.query( "SELECT lobby FROM massivedecks.lobbies WHERE id = $1;", - [lobbyId] + [lobbyId], ); if (get.rowCount < 1) { throw new LobbyClosedError(gameCode); } const { transaction, result } = write( - Lobby.fromJSON(get.rows[0]["lobby"] as Lobby.Lobby) + Lobby.fromJSON(get.rows[0]["lobby"] as Lobby.Lobby), ); if (transaction.lobby) { await client.query( "UPDATE massivedecks.lobbies SET lobby=$2, last_access=NOW() WHERE id = $1;", - [lobbyId, transaction.lobby] + [lobbyId, transaction.lobby], ); } if (transaction.timeouts !== undefined) { for (const timeout of transaction.timeouts) { await client.query( "INSERT INTO massivedecks.timeouts VALUES (DEFAULT, $1, $2, $3)", - [lobbyId, Date.now() + timeout.after, timeout.timeout] + [lobbyId, Date.now() + timeout.after, timeout.timeout], ); } } diff --git a/server/src/ts/store/stores.ts b/server/src/ts/store/stores.ts index b4ee93a9..a885323a 100644 --- a/server/src/ts/store/stores.ts +++ b/server/src/ts/store/stores.ts @@ -1,7 +1,7 @@ -import * as ServerConfig from "../config"; -import { Store } from "../store"; -import { InMemoryStore } from "./in-memory"; -import { PostgresStore } from "./postgres"; +import type * as ServerConfig from "../config.js"; +import type { Store } from "../store.js"; +import { InMemoryStore } from "./in-memory.js"; +import { PostgresStore } from "./postgres.js"; export async function from(config: ServerConfig.Storage): Promise { switch (config.type) { diff --git a/server/src/ts/task.ts b/server/src/ts/task.ts index c963ab4a..17c4f8ce 100644 --- a/server/src/ts/task.ts +++ b/server/src/ts/task.ts @@ -1,7 +1,7 @@ -import { Lobby } from "./lobby"; -import * as Change from "./lobby/change"; -import { GameCode } from "./lobby/game-code"; -import { ServerState } from "./server-state"; +import type { Lobby } from "./lobby.js"; +import * as Change from "./lobby/change.js"; +import type { GameCode } from "./lobby/game-code.js"; +import type { ServerState } from "./server-state.js"; /** * A good base implementation of a task. @@ -18,12 +18,12 @@ export abstract class TaskBase implements Task { protected abstract resolve( lobby: Lobby, work: T, - server: ServerState + server: ServerState, ): Change.Change; protected resolveError( - lobby: Lobby, + _lobby: Lobby, error: Error, - server: ServerState + _server: ServerState, ): Change.Change { throw error; } @@ -35,12 +35,12 @@ export abstract class TaskBase implements Task { } catch (e) { const error = e as Error; await Change.apply(server, this.gameCode, (lobby) => - this.resolveError(lobby, error, server) + this.resolveError(lobby, error, server), ); return; } await Change.apply(server, this.gameCode, (lobby) => - this.resolve(lobby, work, server) + this.resolve(lobby, work, server), ); } } diff --git a/server/src/ts/task/load-deck-summary.ts b/server/src/ts/task/load-deck-summary.ts index eafd2036..678766ee 100644 --- a/server/src/ts/task/load-deck-summary.ts +++ b/server/src/ts/task/load-deck-summary.ts @@ -1,18 +1,19 @@ import Rfc6902 from "rfc6902"; -import * as Event from "../event"; -import * as Configured from "../events/lobby-event/configured"; -import * as Source from "../games/cards/source"; -import { Lobby } from "../lobby"; -import { Change } from "../lobby/change"; -import * as Config from "../lobby/config"; -import { GameCode } from "../lobby/game-code"; -import { ServerState } from "../server-state"; -import * as Task from "../task"; +import type { ReplaceOperation, TestOperation } from "rfc6902/diff.js"; + import { SourceNotFoundError, SourceServiceError, -} from "../errors/action-execution-error"; -import { ReplaceOperation, TestOperation } from "rfc6902/diff"; +} from "../errors/action-execution-error.js"; +import * as Event from "../event.js"; +import * as Configured from "../events/lobby-event/configured.js"; +import type * as Source from "../games/cards/source.js"; +import type { Lobby } from "../lobby.js"; +import type { Change } from "../lobby/change.js"; +import * as Config from "../lobby/config.js"; +import type { GameCode } from "../lobby/game-code.js"; +import type { ServerState } from "../server-state.js"; +import * as Task from "../task.js"; export class LoadDeckSummary extends Task.TaskBase { public readonly source: Source.External; @@ -34,7 +35,7 @@ export class LoadDeckSummary extends Task.TaskBase { private resolveInternal( lobby: Lobby, modify: (source: Config.ConfiguredSource) => void, - server: ServerState + server: ServerState, ): Change { const lobbyConfig = lobby.config; const oldConfig = JSON.parse(JSON.stringify(Config.censor(lobby.config))); @@ -60,7 +61,7 @@ export class LoadDeckSummary extends Task.TaskBase { lobby, events: [ Event.targetAll( - Configured.of([testVersion, ...patch, replaceVersion]) + Configured.of([testVersion, ...patch, replaceVersion]), ), ], }; @@ -72,7 +73,7 @@ export class LoadDeckSummary extends Task.TaskBase { protected resolve( lobby: Lobby, work: Source.Summary, - server: ServerState + server: ServerState, ): Change { return this.resolveInternal( lobby, @@ -81,14 +82,14 @@ export class LoadDeckSummary extends Task.TaskBase { summarised.summary = { ...work, tag: undefined }; } }, - server + server, ); } - protected resolveError( + protected override resolveError( lobby: Lobby, error: Error, - server: ServerState + server: ServerState, ): Change { let reason: Config.FailReason; if (error instanceof SourceNotFoundError) { @@ -101,17 +102,17 @@ export class LoadDeckSummary extends Task.TaskBase { return this.resolveInternal( lobby, (failed) => { - if (!failed.hasOwnProperty("summary")) { + if (!Object.hasOwn(failed, "summary")) { (failed as Config.FailedSource).failure = reason; } }, - server + server, ); } public static *discover( gameCode: GameCode, - lobby: Lobby + lobby: Lobby, ): Iterable { for (const deck of lobby.config.decks) { if (!Config.isFailed(deck) && deck.summary === undefined) { diff --git a/server/src/ts/task/start-game.ts b/server/src/ts/task/start-game.ts index c170b8e4..1d753342 100644 --- a/server/src/ts/task/start-game.ts +++ b/server/src/ts/task/start-game.ts @@ -1,12 +1,13 @@ import wu from "wu"; -import * as Decks from "../games/cards/decks"; -import * as Source from "../games/cards/source"; -import { Game } from "../games/game"; -import { Lobby } from "../lobby"; -import { Change } from "../lobby/change"; -import { GameCode } from "../lobby/game-code"; -import { ServerState } from "../server-state"; -import * as Task from "../task"; + +import type * as Decks from "../games/cards/decks.js"; +import type * as Source from "../games/cards/source.js"; +import { Game } from "../games/game.js"; +import type { Lobby } from "../lobby.js"; +import type { Change } from "../lobby/change.js"; +import type { GameCode } from "../lobby/game-code.js"; +import type { ServerState } from "../server-state.js"; +import * as Task from "../task.js"; export class StartGame extends Task.TaskBase { private readonly decks: Iterable; @@ -19,15 +20,15 @@ export class StartGame extends Task.TaskBase { protected async begin(server: ServerState): Promise { return Promise.all( wu(this.decks).map((deck) => - server.sources.resolver(server.cache, deck).templates() - ) + server.sources.resolver(server.cache, deck).templates(), + ), ); } protected resolve( lobby: Lobby, work: Decks.Templates[], - server: ServerState + server: ServerState, ): Change { if (lobby.game !== undefined && lobby.game.winner === undefined) { // If we have an existing game that isn't finished, we don't try and @@ -40,7 +41,7 @@ export class StartGame extends Task.TaskBase { const { events, timeouts } = lobbyGame.startRound( server, true, - lobbyGame.round + lobbyGame.round, ); lobby.game = lobbyGame; @@ -55,8 +56,8 @@ export class StartGame extends Task.TaskBase { // This is super unlikely timing-wise, and if it happens, the user just has // to click start again. They'll live. public static *discover( - gameCode: GameCode, - lobby: Lobby + _gameCode: GameCode, + _lobby: Lobby, // eslint-disable-next-line @typescript-eslint/no-empty-function ): Iterable {} } diff --git a/server/src/ts/task/tasks.ts b/server/src/ts/task/tasks.ts index 3dbbb7a7..1453daaa 100644 --- a/server/src/ts/task/tasks.ts +++ b/server/src/ts/task/tasks.ts @@ -1,10 +1,10 @@ -import { Lobby } from "../lobby"; -import { GameCode } from "../lobby/game-code"; -import * as Logging from "../logging"; -import { ServerState } from "../server-state"; -import { Task } from "../task"; -import { LoadDeckSummary } from "./load-deck-summary"; -import { StartGame } from "./start-game"; +import type { Lobby } from "../lobby.js"; +import type { GameCode } from "../lobby/game-code.js"; +import * as Logging from "../logging.js"; +import type { ServerState } from "../server-state.js"; +import type { Task } from "../task.js"; +import { LoadDeckSummary } from "./load-deck-summary.js"; +import { StartGame } from "./start-game.js"; interface Discoverable { discover: (gameCode: GameCode, lobby: Lobby) => Iterable; diff --git a/server/src/ts/timeout.ts b/server/src/ts/timeout.ts index 12edcb71..175bab96 100644 --- a/server/src/ts/timeout.ts +++ b/server/src/ts/timeout.ts @@ -1,13 +1,13 @@ -import { Lobby } from "./lobby"; -import * as Change from "./lobby/change"; -import { GameCode } from "./lobby/game-code"; -import { ServerState } from "./server-state"; -import * as FinishedPlaying from "./timeout/finished-playing"; -import * as RoundStageTimerDone from "./timeout/round-stage-timer-done"; -import * as RoundStart from "./timeout/round-start"; -import * as UserDisconnect from "./timeout/user-disconnect"; -import * as Logging from "./logging"; -import * as FinishedRevealing from "./timeout/finished-revealing"; +import type { Lobby } from "./lobby.js"; +import * as Change from "./lobby/change.js"; +import type { GameCode } from "./lobby/game-code.js"; +import * as Logging from "./logging.js"; +import type { ServerState } from "./server-state.js"; +import * as FinishedPlaying from "./timeout/finished-playing.js"; +import * as FinishedRevealing from "./timeout/finished-revealing.js"; +import * as RoundStageTimerDone from "./timeout/round-stage-timer-done.js"; +import * as RoundStart from "./timeout/round-start.js"; +import * as UserDisconnect from "./timeout/user-disconnect.js"; /** * A timeout represents something that must happen after a delay in-game. @@ -56,13 +56,13 @@ export const handler: Handler = (server, timeout, gameCode, lobby) => { export async function handle(server: ServerState): Promise { for await (const { id, lobby, timeout } of server.store.timedOut()) { Logging.logger.debug( - `Timeout executing: ${id} (${JSON.stringify(timeout)}) in ${lobby}` + `Timeout executing: ${id} (${JSON.stringify(timeout)}) in ${lobby}`, ); await Change.apply( server, lobby, (lobbyState) => handler(server, timeout, lobby, lobbyState), - id + id, ); } } @@ -71,5 +71,5 @@ export type Handler = ( server: ServerState, timeout: T, gameCode: GameCode, - lobby: Lobby + lobby: Lobby, ) => Change.Change; diff --git a/server/src/ts/timeout/finished-playing.ts b/server/src/ts/timeout/finished-playing.ts index dcbc5cc9..07b22059 100644 --- a/server/src/ts/timeout/finished-playing.ts +++ b/server/src/ts/timeout/finished-playing.ts @@ -1,8 +1,9 @@ import wu from "wu"; -import * as Round from "../games/game/round"; -import * as Timeout from "../timeout"; -import * as Util from "../util"; -import * as Rules from "../games/rules"; + +import type * as Round from "../games/game/round.js"; +import type * as Rules from "../games/rules.js"; +import type * as Timeout from "../timeout.js"; +import * as Util from "../util.js"; /** * Indicates that the round should start the revealing phase if it is appropriate diff --git a/server/src/ts/timeout/finished-revealing.ts b/server/src/ts/timeout/finished-revealing.ts index ee4357ac..948bf54a 100644 --- a/server/src/ts/timeout/finished-revealing.ts +++ b/server/src/ts/timeout/finished-revealing.ts @@ -1,5 +1,5 @@ -import * as Timeout from "../timeout"; -import * as Rules from "../games/rules"; +import type * as Rules from "../games/rules.js"; +import type * as Timeout from "../timeout.js"; /** * Indicates that the round should start the judging phase if it is appropriate @@ -17,10 +17,10 @@ export const of = (stages: Rules.Stages): Timeout.After => ({ }); export const handle: Timeout.Handler = ( - server, - timeout, - gameCode, - lobby + _server, + _timeout, + _gameCode, + lobby, ) => { const game = lobby.game; if (game === undefined) { diff --git a/server/src/ts/timeout/round-stage-timer-done.ts b/server/src/ts/timeout/round-stage-timer-done.ts index 70a44a86..b693e6c3 100644 --- a/server/src/ts/timeout/round-stage-timer-done.ts +++ b/server/src/ts/timeout/round-stage-timer-done.ts @@ -1,13 +1,13 @@ -import { dealWithLostPlayer } from "../action/game-action/set-presence"; -import * as Event from "../event"; -import * as StageTimerDone from "../events/game-event/stage-timer-done"; -import * as Round from "../games/game/round"; -import * as Rules from "../games/rules"; -import * as Lobby from "../lobby"; -import * as Change from "../lobby/change"; -import * as Timeout from "../timeout"; -import * as Util from "../util"; -import { Stages } from "../games/rules"; +import { dealWithLostPlayer } from "../action/game-action/set-presence.js"; +import * as Event from "../event.js"; +import * as StageTimerDone from "../events/game-event/stage-timer-done.js"; +import * as Round from "../games/game/round.js"; +import type { Stages } from "../games/rules.js"; +import type * as Rules from "../games/rules.js"; +import type * as Lobby from "../lobby.js"; +import * as Change from "../lobby/change.js"; +import type * as Timeout from "../timeout.js"; +import * as Util from "../util.js"; /** * Indicates that the user should be marked as disconnected if they still are. @@ -49,7 +49,7 @@ function stageDuration(stage: Round.Stage, stages: Stages): number | undefined { */ export const ifEnabled = ( round: Round.Round, - stages: Rules.Stages + stages: Rules.Stages, ): Timeout.After | undefined => { const afterSeconds = stageDuration(round.stage, stages); if (afterSeconds === undefined) { @@ -69,7 +69,7 @@ export const handle: Timeout.Handler = ( server, timeout, gameCode, - lobby + lobby, ) => { const game = lobby.game; if (game === undefined) { @@ -99,7 +99,7 @@ export const handle: Timeout.Handler = ( }; case "Hard": return Change.reduce(waitingFor, lobby as Lobby.WithActiveGame, (l, p) => - dealWithLostPlayer(server, l, p) + dealWithLostPlayer(server, l, p), ); default: Util.assertNever(stages.timeLimitMode); diff --git a/server/src/ts/timeout/round-start.ts b/server/src/ts/timeout/round-start.ts index 999b7306..5faa9b8c 100644 --- a/server/src/ts/timeout/round-start.ts +++ b/server/src/ts/timeout/round-start.ts @@ -1,7 +1,7 @@ -import * as Event from "../event"; -import * as GameEnded from "../events/game-event/game-ended"; -import * as Lobby from "../lobby"; -import * as Timeout from "../timeout"; +import * as Event from "../event.js"; +import * as GameEnded from "../events/game-event/game-ended.js"; +import * as Lobby from "../lobby.js"; +import type * as Timeout from "../timeout.js"; /** * Indicates that the round should start if it is still appropriate to do so. @@ -18,7 +18,7 @@ export const handle: Timeout.Handler = ( server, timeout, gameCode, - inLobby + inLobby, ) => { if (Lobby.hasActiveGame(inLobby)) { const lobbyGame = inLobby.game; diff --git a/server/src/ts/timeout/user-disconnect.ts b/server/src/ts/timeout/user-disconnect.ts index 08b26657..98d2b7a5 100644 --- a/server/src/ts/timeout/user-disconnect.ts +++ b/server/src/ts/timeout/user-disconnect.ts @@ -1,9 +1,10 @@ -import * as Event from "../event"; -import * as ConnectionChanged from "../events/lobby-event/connection-changed"; -import * as Timeout from "../timeout"; -import * as User from "../user"; import wu from "wu"; +import * as Event from "../event.js"; +import * as ConnectionChanged from "../events/lobby-event/connection-changed.js"; +import type * as Timeout from "../timeout.js"; +import type * as User from "../user.js"; + /** * Indicates that the user should be marked as disconnected if they still are. */ @@ -21,7 +22,7 @@ export const handle: Timeout.Handler = ( server, timeout, gameCode, - lobby + lobby, ) => { const id = timeout.user; const sockets = server.socketManager.sockets.get(gameCode, id); diff --git a/server/src/ts/user.ts b/server/src/ts/user.ts index 6f190ce3..aad84efb 100644 --- a/server/src/ts/user.ts +++ b/server/src/ts/user.ts @@ -1,4 +1,4 @@ -import { RegisterUser } from "./action/initial/register-user"; +import type { RegisterUser } from "./action/initial/register-user.js"; /** A user in a lobby.*/ export interface User { @@ -80,7 +80,7 @@ export const isSpectating: (user: User) => boolean = (user) => export const create = ( registration: RegisterUser, role: Role, - privilege: Privilege = "Unprivileged" + privilege: Privilege = "Unprivileged", ): User => ({ name: registration.name, presence: "Joined", diff --git a/server/src/ts/user/token.ts b/server/src/ts/user/token.ts index de4b91d5..6e765ea0 100644 --- a/server/src/ts/user/token.ts +++ b/server/src/ts/user/token.ts @@ -1,10 +1,11 @@ import jwt from "jsonwebtoken"; + import { IncorrectIssuerError, InvalidAuthenticationError, -} from "../errors/authentication"; -import { GameCode } from "../lobby/game-code"; -import * as User from "../user"; +} from "../errors/authentication.js"; +import type { GameCode } from "../lobby/game-code.js"; +import type * as User from "../user.js"; /** * A token that contains the encoded claims of a user. @@ -33,7 +34,7 @@ export interface Claims { export const create = ( tokenClaims: Claims, issuer: string, - secret: string + secret: string, ): Token => jwt.sign(tokenClaims, secret, { algorithm: "HS256", issuer: issuer }); @@ -52,7 +53,7 @@ export function validate(token: Token, issuer: string, secret: string): Claims { }) as Claims; } catch (e) { const error = e as Error; - if (error.hasOwnProperty("name") && error.name === "JsonWebTokenError") { + if (Object.hasOwn(error, "name") && error.name === "JsonWebTokenError") { if (error.message.startsWith("jwt issuer invalid.")) { throw new IncorrectIssuerError(); } else if (error.message.startsWith("invalid signature")) { diff --git a/server/src/ts/util.ts b/server/src/ts/util.ts index b16fc430..181d8782 100644 --- a/server/src/ts/util.ts +++ b/server/src/ts/util.ts @@ -2,13 +2,13 @@ * Check if the given value is an iterable one. */ export const isIterable = ( - object: T | Iterable + object: T | Iterable, ): object is Iterable => object !== null && typeof (object as Iterable)[Symbol.iterator] === "function"; const isSingleArgument = ( - f: ((a: A, b: B) => C) | ((b: B) => C) + f: ((a: A, b: B) => C) | ((b: B) => C), ): f is (b: B) => C => f.length === 1; /** @@ -17,7 +17,7 @@ const isSingleArgument = ( */ export function mapObjectValues( obj: O, - f: ((key: string, value: V) => U) | ((value: V) => U) + f: ((key: string, value: V) => U) | ((value: V) => U), ): { [P in keyof O]: U } { const newObj: { [key: string]: U } = {}; for (const [key, value] of Object.entries(obj)) { @@ -29,9 +29,9 @@ export function mapObjectValues( /** * Create an object from the given entries. */ -export function entriesToObject( - entries: Iterable<[string, T]> -): { [key: string]: T } { +export function entriesToObject(entries: Iterable<[string, T]>): { + [key: string]: T; +} { const obj: { [key: string]: T } = {}; for (const [key, value] of entries) { obj[key] = value; @@ -55,7 +55,7 @@ export const mapToObject = (map: Map): { [key: string]: T } => */ export function counts( iterable: Iterable, - predicates: { [P in keyof U]: (value: T) => boolean } + predicates: { [P in keyof U]: (value: T) => boolean }, ): { [P in keyof U]: number } { const keys = Object.keys(predicates) as (keyof U)[]; const amounts = mapObjectValues(predicates, () => 0); @@ -86,7 +86,7 @@ export function assertNever(value: never): never { */ export function findIs( iterable: Iterable, - predicate: (item: T) => item is U + predicate: (item: T) => item is U, ): U | undefined { for (const item of iterable) { if (predicate(item)) { @@ -103,7 +103,7 @@ export function findIs( export function shuffle(items: T[]): void { for (let index = items.length - 1; index > 0; index -= 1) { const random = Math.floor(Math.random() * (index + 1)); - [items[index], items[random]] = [items[random], items[index]]; + [items[index], items[random]] = [items[random] as T, items[index] as T]; } } @@ -137,7 +137,7 @@ export function setEquals(a: Set, b: Set): boolean { * Return an item that may be undefined as either an iterable of nothing or an iterable of just that item. */ export function* asIterable( - maybeItem: T | undefined + maybeItem: T | undefined, ): Iterable | undefined { if (maybeItem !== undefined) { yield maybeItem; @@ -148,7 +148,7 @@ export function* asIterable( * Return an item that may be undefined as either a undefined or an iterable of just that item. */ export function asOptionalIterable( - maybeItem: T | undefined + maybeItem: T | undefined, ): Iterable | undefined { if (maybeItem === undefined) { return undefined; diff --git a/server/src/ts/util/postgres.ts b/server/src/ts/util/postgres.ts index 18486df8..e8349a07 100644 --- a/server/src/ts/util/postgres.ts +++ b/server/src/ts/util/postgres.ts @@ -1,5 +1,6 @@ import Pg from "pg"; -import * as Logging from "../logging"; + +import * as Logging from "../logging.js"; /** * A version for the database. Undefined if none exists. @@ -12,7 +13,7 @@ export type Version = number | undefined; * exception. */ export type Upgrades = ( - version: Version + version: Version, ) => Upgrade | undefined; /** @@ -35,20 +36,18 @@ export class Postgres { public async ensureCurrent(): Promise { await this.inTransaction(async (client) => { let version = await this.findVersion(client); - while (true) { - const upgrade = this.upgrades(version); - if (upgrade === undefined) { - return; - } + let upgrade = this.upgrades(version); + while (upgrade !== undefined) { const oldVersion = version; version = await upgrade.apply(client); if (oldVersion == undefined) { Logging.logger.info(`Created '${this.schema}' at '${version}'`); } else { Logging.logger.info( - `Upgraded '${this.schema}' from '${oldVersion}' to '${version}'.` + `Upgraded '${this.schema}' from '${oldVersion}' to '${version}'.`, ); } + upgrade = this.upgrades(version); } }); } @@ -58,7 +57,7 @@ export class Postgres { * done. */ public async withClient( - f: (client: Pg.PoolClient) => Promise + f: (client: Pg.PoolClient) => Promise, ): Promise { const client = await this.pool.connect(); try { @@ -73,7 +72,7 @@ export class Postgres { * done. */ public async *withClientIterator( - f: (client: Pg.PoolClient) => AsyncIterableIterator + f: (client: Pg.PoolClient) => AsyncIterableIterator, ): AsyncIterableIterator { const client = await this.pool.connect(); try { @@ -89,7 +88,7 @@ export class Postgres { * @param f the function to execute. */ public async inTransaction( - f: (client: Pg.PoolClient) => Promise + f: (client: Pg.PoolClient) => Promise, ): Promise { return await this.withClient(async (client) => { await client.query("BEGIN;"); @@ -106,11 +105,11 @@ export class Postgres { private async findVersion(client: Pg.PoolClient): Promise { const exists = await client.query( - `SELECT EXISTS (SELECT FROM information_schema.schemata WHERE schema_name = '${this.schema}');` + `SELECT EXISTS (SELECT FROM information_schema.schemata WHERE schema_name = '${this.schema}');`, ); if (exists.rows[0]["exists"]) { const rows = await client.query( - `SELECT version FROM ${this.schema}.meta;` + `SELECT version FROM ${this.schema}.meta;`, ); const row = rows.rows[0]; return row["version"]; diff --git a/server/tsconfig.json b/server/tsconfig.json index 428097cf..b36e0863 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -1,24 +1,31 @@ { - "include": ["./src/ts/**/*"], + "$schema": "https://json.schemastore.org/tsconfig", + + "include": ["src/**/*"], + "compilerOptions": { - "target": "esnext", - "module": "esnext", + "lib": ["es2022"], + "module": "nodenext", + "target": "es2022", + "moduleResolution": "nodenext", + "sourceMap": true, - "outDir": "./dist", - "incremental": true, + "rootDir": "src/ts", + "outDir": "dist", "removeComments": true, - "resolveJsonModule": true, + "newLine": "lf", + "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "strictBindCallApply": true, - "strictPropertyInitialization": true, - "noImplicitThis": true, - "alwaysStrict": true, - "noImplicitReturns": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, - "moduleResolution": "node", - "allowSyntheticDefaultImports": true + "noImplicitOverride": true, + "noImplicitReturns": true, + "noPropertyAccessFromIndexSignature": true, + "noUncheckedIndexedAccess": true, + "importsNotUsedAsValues": "error", + "checkJs": true } }