diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5c7ba8a..d76bd72 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -19,6 +19,8 @@ jobs: run: bun run check - name: ESLint run: bun run lint + - name: Typecheck + run: bun run typecheck - name: Run tests run: bun run test deploy: diff --git a/app/components/TeamJoinModal.vue b/app/components/TeamJoinModal.vue index 810aec9..00473aa 100644 --- a/app/components/TeamJoinModal.vue +++ b/app/components/TeamJoinModal.vue @@ -77,6 +77,7 @@ async function submit() {

Join a team

+ @@ -227,7 +227,7 @@ async function addUser() { label="End?" :model-value="!!formData.cloned.value.end" @update:model-value=" - (newVal) => + (newVal: boolean) => (formData.cloned.value!.end = newVal ? new Date() : null) " /> diff --git a/app/pages/hunt/[huntSlug]/pictures.vue b/app/pages/hunt/[huntSlug]/pictures.vue index 61b9bce..88ff2d6 100644 --- a/app/pages/hunt/[huntSlug]/pictures.vue +++ b/app/pages/hunt/[huntSlug]/pictures.vue @@ -29,7 +29,13 @@ type Picture = { const { data, refresh, error } = await useFetch(`/api/hunt/${id}/diashow`, { transform: (input) => { if (!input || input.quests.length <= 0) - return { id: 0, name: '', cols: [] as Picture[][], count: 0 }; + return { + id: 0, + name_de: '', + name_en: '', + cols: [] as Picture[][], + count: 0 + }; const { quests, ...rest } = input; const pictures: Picture[] = []; diff --git a/bun.lock b/bun.lock index f7dd715..dfd18c3 100644 --- a/bun.lock +++ b/bun.lock @@ -29,11 +29,12 @@ }, "devDependencies": { "@nuxt/devtools": "^2.6.2", + "@types/bun": "^1.2.20", "prettier": "^3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "prettier-plugin-tailwindcss": "^0.6.14", "prisma": "^6.13.0", - "vue-tsc": "^3.0.4", + "vue-tsc": "^3.0.6", }, }, }, @@ -542,6 +543,8 @@ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="], + "@types/bun": ["@types/bun@1.2.20", "", { "dependencies": { "bun-types": "1.2.20" } }, "sha512-dX3RGzQ8+KgmMw7CsW4xT5ITBSCrSbfHc36SNT31EOUg/LA9JWq0VDdEXDRSe1InVWpd2yLUM1FUF/kEOyTzYA=="], + "@types/bytes": ["@types/bytes@3.1.5", "", {}, "sha512-VgZkrJckypj85YxEsEavcMmmSOIzkUHqWmM4CCyia5dc54YwsXzJ5uT4fYxBQNEXx+oF1krlhgCbvfubXqZYsQ=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], @@ -558,6 +561,8 @@ "@types/pluralize": ["@types/pluralize@0.0.33", "", {}, "sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg=="], + "@types/react": ["@types/react@19.1.10", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg=="], + "@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="], "@types/triple-beam": ["@types/triple-beam@1.3.5", "", {}, "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="], @@ -632,11 +637,11 @@ "@vitejs/plugin-vue-jsx": ["@vitejs/plugin-vue-jsx@5.0.1", "", { "dependencies": { "@babel/core": "^7.27.7", "@babel/plugin-transform-typescript": "^7.27.1", "@rolldown/pluginutils": "^1.0.0-beta.21", "@vue/babel-plugin-jsx": "^1.4.0" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", "vue": "^3.0.0" } }, "sha512-X7qmQMXbdDh+sfHUttXokPD0cjPkMFoae7SgbkF9vi3idGUKmxLcnU2Ug49FHwiKXebfzQRIm5yK3sfCJzNBbg=="], - "@volar/language-core": ["@volar/language-core@2.4.20", "", { "dependencies": { "@volar/source-map": "2.4.20" } }, "sha512-dRDF1G33xaAIDqR6+mXUIjXYdu9vzSxlMGfMEwBxQsfY/JMUEXSpLTR057oTKlUQ2nIvCmP9k94A8h8z2VrNSA=="], + "@volar/language-core": ["@volar/language-core@2.4.23", "", { "dependencies": { "@volar/source-map": "2.4.23" } }, "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ=="], - "@volar/source-map": ["@volar/source-map@2.4.20", "", {}, "sha512-mVjmFQH8mC+nUaVwmbxoYUy8cww+abaO8dWzqPUjilsavjxH0jCJ3Mp8HFuHsdewZs2c+SP+EO7hCd8Z92whJg=="], + "@volar/source-map": ["@volar/source-map@2.4.23", "", {}, "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q=="], - "@volar/typescript": ["@volar/typescript@2.4.20", "", { "dependencies": { "@volar/language-core": "2.4.20", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-Oc4DczPwQyXcVbd+5RsNEqX6ia0+w3p+klwdZQ6ZKhFjWoBP9PCPQYlKYRi/tDemWphW93P/Vv13vcE9I9D2GQ=="], + "@volar/typescript": ["@volar/typescript@2.4.23", "", { "dependencies": { "@volar/language-core": "2.4.23", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag=="], "@vue-macros/common": ["@vue-macros/common@1.16.1", "", { "dependencies": { "@vue/compiler-sfc": "^3.5.13", "ast-kit": "^1.4.0", "local-pkg": "^1.0.0", "magic-string-ast": "^0.7.0", "pathe": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "vue": "^2.7.0 || ^3.2.25" }, "optionalPeers": ["vue"] }, "sha512-Pn/AWMTjoMYuquepLZP813BIcq8DTZiNCoaceuNlvaYuOTd8DqBZWc5u0uOMQZMInwME1mdSmmBAcTluiV9Jtg=="], @@ -664,7 +669,7 @@ "@vue/devtools-shared": ["@vue/devtools-shared@7.7.7", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw=="], - "@vue/language-core": ["@vue/language-core@3.0.4", "", { "dependencies": { "@volar/language-core": "2.4.20", "@vue/compiler-dom": "^3.5.0", "@vue/compiler-vue2": "^2.7.16", "@vue/shared": "^3.5.0", "alien-signals": "^2.0.5", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1", "picomatch": "^4.0.2" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-BvueED4LfBCSNH66eeUQk37MQCb7hjdezzGgxniM0LbriW53AJIyLorgshAtStmjfsAuOCcTl/c1b+nz/ye8xQ=="], + "@vue/language-core": ["@vue/language-core@3.0.6", "", { "dependencies": { "@volar/language-core": "2.4.23", "@vue/compiler-dom": "^3.5.0", "@vue/compiler-vue2": "^2.7.16", "@vue/shared": "^3.5.0", "alien-signals": "^2.0.5", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1", "picomatch": "^4.0.2" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-e2RRzYWm+qGm8apUHW1wA5RQxzNhkqbbKdbKhiDUcmMrNAZGyM8aTiL3UrTqkaFI5s7wJRGGrp4u3jgusuBp2A=="], "@vue/reactivity": ["@vue/reactivity@3.5.18", "", { "dependencies": { "@vue/shared": "3.5.18" } }, "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg=="], @@ -790,6 +795,8 @@ "builtin-modules": ["builtin-modules@5.0.0", "", {}, "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg=="], + "bun-types": ["bun-types@1.2.20", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-pxTnQYOrKvdOwyiyd/7sMt9yFOenN004Y6O4lCcCUoKVej48FS5cvTw9geRaEcB9TsDZaJKAxPTVvi8tFsVuXA=="], + "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], "bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="], @@ -2118,7 +2125,7 @@ "vue-router": ["vue-router@4.5.1", "", { "dependencies": { "@vue/devtools-api": "^6.6.4" }, "peerDependencies": { "vue": "^3.2.0" } }, "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw=="], - "vue-tsc": ["vue-tsc@3.0.4", "", { "dependencies": { "@volar/typescript": "2.4.20", "@vue/language-core": "3.0.4" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "./bin/vue-tsc.js" } }, "sha512-kZmSEjGtROApVBuaIcoprrXZsFNGon5ggkTJokmhQ/H1hMzCFRPQ0Ed8IHYFsmYJYvHBcdmEQVGVcRuxzPzNbw=="], + "vue-tsc": ["vue-tsc@3.0.6", "", { "dependencies": { "@volar/typescript": "2.4.23", "@vue/language-core": "3.0.6" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "./bin/vue-tsc.js" } }, "sha512-Tbs8Whd43R2e2nxez4WXPvvdjGbW24rOSgRhLOHXzWiT4pcP4G7KeWh0YCn18rF4bVwv7tggLLZ6MJnO6jXPBg=="], "vuestic-ui": ["vuestic-ui@1.10.3", "", { "dependencies": { "@floating-ui/vue": "^1.0.1", "@types/lodash": "^4.14.161", "cleave.js": "^1.6.0", "lodash": "^4.17.21" }, "peerDependencies": { "vue": "^3.0.4" } }, "sha512-E8Y6E7dg08+yrN+0UfIb7pffH6dX9hBrLhjTR74w9T8iXlkw8t5ULPwnzIXxrTxPbk/HvDDs1s++bMw/S0OFmA=="], diff --git a/package.json b/package.json index c4b21b3..5326260 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "test": "bun test", "lint": "eslint .", "lint:fix": "eslint . --fix", + "typecheck": "nuxt typecheck", "dev:up": "docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d", "dev:down": "docker-compose -f docker-compose.yml -f docker-compose.dev.yml down" }, @@ -42,10 +43,11 @@ }, "devDependencies": { "@nuxt/devtools": "^2.6.2", + "@types/bun": "^1.2.20", "prettier": "^3.6.2", "prettier-plugin-organize-imports": "^4.2.0", "prettier-plugin-tailwindcss": "^0.6.14", "prisma": "^6.13.0", - "vue-tsc": "^3.0.4" + "vue-tsc": "^3.0.6" } } diff --git a/server/api/hunt/[huntId]/join.post.ts b/server/api/hunt/[huntId]/join.post.ts index 803e29f..0eafa0d 100644 --- a/server/api/hunt/[huntId]/join.post.ts +++ b/server/api/hunt/[huntId]/join.post.ts @@ -1,4 +1,3 @@ -import { requireUserSession } from '#imports'; import { useValidatedBody, useValidatedParams, z, zh } from 'h3-zod'; import prisma from '~~/lib/prisma'; diff --git a/server/api/quest/[questId]/submit.post.ts b/server/api/quest/[questId]/submit.post.ts index 8f4811e..cebff88 100644 --- a/server/api/quest/[questId]/submit.post.ts +++ b/server/api/quest/[questId]/submit.post.ts @@ -76,11 +76,11 @@ export default defineEventHandler(async (event) => { if (quest.hunt.teams.length <= 0) { throw createError({ status: 403, statusText: 'No team yet!' }); } - if (quest.answers.length > 0 && quest.answers[0].final) { + if (quest.answers.length > 0 && quest.answers![0]?.final) { throw createError({ status: 418, statusText: 'Already marked as final!' }); } - const team = quest.hunt.teams[0]; + const team = quest.hunt.teams![0]!; const body = await readFormData(event); const formDataObj = Object.fromEntries(body.entries()); @@ -101,7 +101,7 @@ export default defineEventHandler(async (event) => { const prevAnswer = quest.answers.length > 0 - ? quest.answers[0] + ? quest.answers![0]! : { updatedAt: null, id: null diff --git a/shared/auth.d.ts b/shared/auth.d.ts index b392d5d..73ac5b3 100644 --- a/shared/auth.d.ts +++ b/shared/auth.d.ts @@ -1,7 +1,8 @@ import type { User as PUser } from '@prisma/client'; declare module '#auth-utils' { - type User = Pick; + // eslint-disable-next-line + interface User extends Pick {} interface UserSession { user: User; loggedInAt: string | Date; diff --git a/shared/utils/slugcheck.test.ts b/shared/utils/slugcheck.test.ts index ccaf151..a8ddbdf 100644 --- a/shared/utils/slugcheck.test.ts +++ b/shared/utils/slugcheck.test.ts @@ -1,3 +1,4 @@ +// @ts-expect-error weird bun import with nuxt import { describe, expect, it } from 'bun:test'; import { checkSlug, parseSlug, type SlugRecord } from './slugcheck'; diff --git a/shared/utils/slugcheck.ts b/shared/utils/slugcheck.ts index 4c787c4..81cc3a7 100644 --- a/shared/utils/slugcheck.ts +++ b/shared/utils/slugcheck.ts @@ -1,4 +1,4 @@ -import type { RouteParamsGeneric } from '#vue-router'; +import type { RouteParamsGeneric } from 'vue-router'; export type Slug = { name: string; id: number }; export type SlugRecord = Record; @@ -16,12 +16,13 @@ export function parseSlug(slug: string): null | Slug { const { name, id } = result.groups!; return { - name: name - .substring(0, name.length - 1) - .split('-') - .map((w) => `${w.substring(0, 1).toUpperCase()}${w.substring(1)}`) - .join(' '), - id: parseInt(id) + name: + name + ?.substring(0, name.length - 1) + .split('-') + .map((w) => `${w.substring(0, 1).toUpperCase()}${w.substring(1)}`) + .join(' ') ?? '', + id: parseInt(id!) }; }