From 379765b28fcc00542cefd98a4d4096dc9522d769 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Fri, 2 May 2025 02:12:08 +0200 Subject: [PATCH] Fix typechecking --- api/bun.lock | 10 ++++---- api/package.json | 2 +- api/src/controllers/entries.ts | 14 ++++++----- api/src/controllers/profiles/history.ts | 31 +++++++++++++------------ api/src/controllers/profiles/nextup.ts | 4 +++- api/src/models/entry/index.ts | 3 +-- api/src/models/examples/dune-1984.ts | 1 - api/src/models/examples/dune-2021.ts | 1 - api/src/models/show.ts | 7 +++--- api/src/models/watchlist.ts | 1 + api/tests/movies/get-movie.test.ts | 2 +- 11 files changed, 39 insertions(+), 37 deletions(-) diff --git a/api/bun.lock b/api/bun.lock index 912a6649..a1983672 100644 --- a/api/bun.lock +++ b/api/bun.lock @@ -8,7 +8,7 @@ "blurhash": "^2.0.5", "drizzle-kit": "^0.31.0", "drizzle-orm": "0.43.1", - "elysia": "^1.3.0-exp.71", + "elysia": "^1.2.25", "jose": "^6.0.10", "parjs": "^1.3.9", "pg": "^8.15.6", @@ -165,16 +165,12 @@ "drizzle-orm": ["drizzle-orm@0.43.1", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-dUcDaZtE/zN4RV/xqGrVSMpnEczxd5cIaoDeor7Zst9wOe/HzC/7eAaulywWGYXdDEc9oBPMjayVEDg0ziTLJA=="], - "elysia": ["elysia@1.3.0-exp.71", "", { "dependencies": { "@sinclair/typebox": "^0.34.33", "cookie": "^1.0.2", "exact-mirror": "0.1.1", "fast-decode-uri-component": "^1.0.1", "openapi-types": "^12.1.3" }, "peerDependencies": { "file-type": ">= 20.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["file-type", "typescript"] }, "sha512-jL7z5OzJgs8pCzEXRmzzYu972S9hILiab7bVD3VBJHAE/9EMdG5uzxWA++3rxJXPEW7HvK3E31zaJKv6TtKgqA=="], + "elysia": ["elysia@1.2.25", "", { "dependencies": { "@sinclair/typebox": "^0.34.27", "cookie": "^1.0.2", "memoirist": "^0.3.0", "openapi-types": "^12.1.3" }, "peerDependencies": { "typescript": ">= 5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-WsdQpORJvb4uszzeqYT0lg97knw1iBW1NTzJ1Jm57tiHg+DfAotlWXYbjmvQ039ssV0fYELDHinLLoUazZkEHg=="], "esbuild": ["esbuild@0.25.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.3", "@esbuild/android-arm": "0.25.3", "@esbuild/android-arm64": "0.25.3", "@esbuild/android-x64": "0.25.3", "@esbuild/darwin-arm64": "0.25.3", "@esbuild/darwin-x64": "0.25.3", "@esbuild/freebsd-arm64": "0.25.3", "@esbuild/freebsd-x64": "0.25.3", "@esbuild/linux-arm": "0.25.3", "@esbuild/linux-arm64": "0.25.3", "@esbuild/linux-ia32": "0.25.3", "@esbuild/linux-loong64": "0.25.3", "@esbuild/linux-mips64el": "0.25.3", "@esbuild/linux-ppc64": "0.25.3", "@esbuild/linux-riscv64": "0.25.3", "@esbuild/linux-s390x": "0.25.3", "@esbuild/linux-x64": "0.25.3", "@esbuild/netbsd-arm64": "0.25.3", "@esbuild/netbsd-x64": "0.25.3", "@esbuild/openbsd-arm64": "0.25.3", "@esbuild/openbsd-x64": "0.25.3", "@esbuild/sunos-x64": "0.25.3", "@esbuild/win32-arm64": "0.25.3", "@esbuild/win32-ia32": "0.25.3", "@esbuild/win32-x64": "0.25.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q=="], "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], - "exact-mirror": ["exact-mirror@0.1.1", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-jygrs/z9JT3UBDVPsu4vLy8gqtTLTxVzoxLmDzkVXHizRGixDMdkdLF98ChZxsqHL0F7IcpTf8GUFRqa2qt3uw=="], - - "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="], - "get-tsconfig": ["get-tsconfig@4.10.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A=="], "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="], @@ -183,6 +179,8 @@ "jose": ["jose@6.0.10", "", {}, "sha512-skIAxZqcMkOrSwjJvplIPYrlXGpxTPnro2/QWTDCxAdWQrSTV5/KqspMWmi5WAx5+ULswASJiZ0a+1B/Lxt9cw=="], + "memoirist": ["memoirist@0.3.0", "", {}, "sha512-wR+4chMgVPq+T6OOsk40u9Wlpw1Pjx66NMNiYxCQQ4EUJ7jDs3D9kTCeKdBOkvAiqXlHLVJlvYL01PvIJ1MPNg=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "node-addon-api": ["node-addon-api@8.3.1", "", {}, "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA=="], diff --git a/api/package.json b/api/package.json index 74d4e81e..1206b686 100644 --- a/api/package.json +++ b/api/package.json @@ -13,7 +13,7 @@ "blurhash": "^2.0.5", "drizzle-kit": "^0.31.0", "drizzle-orm": "0.43.1", - "elysia": "^1.3.0-exp.71", + "elysia": "^1.2.25", "jose": "^6.0.10", "parjs": "^1.3.9", "pg": "^8.15.6", diff --git a/api/src/controllers/entries.ts b/api/src/controllers/entries.ts index 478b7538..dce5edae 100644 --- a/api/src/controllers/entries.ts +++ b/api/src/controllers/entries.ts @@ -143,13 +143,15 @@ export const entryVideosQ = db export const mapProgress = ({ aliased }: { aliased: boolean }) => { const { time, percent, playedDate, videoId } = getColumns(entryProgressQ); const ret = { - time: coalesce(time, sql`0`), - percent: coalesce(percent, sql`0`), - playedDate: sql`${playedDate}`, - videoId: sql`${videoId}`, + time: coalesce(time, sql`0`), + percent: coalesce(percent, sql`0`), + playedDate: sql`${playedDate}`, + videoId: sql`${videoId}`, }; if (!aliased) return ret; - return Object.fromEntries(Object.entries(ret).map(([k, v]) => [k, v.as(k)])); + return Object.fromEntries( + Object.entries(ret).map(([k, v]) => [k, v.as(k)]), + ) as unknown as typeof ret; }; export async function getEntries({ @@ -197,7 +199,7 @@ export async function getEntries({ videos: entryVideosQ.videos, progress: mapProgress({ aliased: true }), // specials don't have an `episodeNumber` but a `number` field. - number: episodeNumber, + number: sql`${episodeNumber}`, // merge `extraKind` into `kind` kind: sql`case when ${kind} = 'extra' then ${extraKind} else ${kind}::text end`.as( diff --git a/api/src/controllers/profiles/history.ts b/api/src/controllers/profiles/history.ts index f317e033..fdf239c2 100644 --- a/api/src/controllers/profiles/history.ts +++ b/api/src/controllers/profiles/history.ts @@ -18,6 +18,7 @@ import { processLanguages, } from "~/models/utils"; import { desc } from "~/models/utils/descriptions"; +import type { WatchlistStatus } from "~/models/watchlist"; import { entryFilters, entryProgressQ, @@ -186,12 +187,12 @@ export const historyH = new Elysia({ tags: ["profiles"] }) .select( db .select({ - profilePk: sql`${profilePk}`, + profilePk: sql`${profilePk}`.as("profilePk"), entryPk: entries.pk, videoPk: videos.pk, - percent: sql`hist.percent`, - time: sql`hist.time`, - playedDate: sql`hist.playedDate`, + percent: sql`hist.percent`.as("percent"), + time: sql`hist.time`.as("time"), + playedDate: sql`hist.playedDate`.as("playedDate"), }) .from(hist) .innerJoin(entries, valEqEntries) @@ -248,9 +249,9 @@ export const historyH = new Elysia({ tags: ["profiles"] }) .select( db .select({ - profilePk: sql`${profilePk}`, + profilePk: sql`${profilePk}`.as("profilePk"), showPk: entries.showPk, - status: sql` + status: sql` case when hist.percent >= 95 @@ -258,35 +259,35 @@ export const historyH = new Elysia({ tags: ["profiles"] }) then 'completed'::watchlist_status else 'watching'::watchlist_status end - `, + `.as("status"), seenCount: sql` case when ${entries.kind} = 'movie' then hist.percent when hist.percent >= 95 then 1 else 0 end - `, + `.as("seen_count"), nextEntry: sql` case when hist.percent >= 95 then ${nextEntryQ.pk} else ${entries.pk} end - `, - score: sql`null`, - startedAt: sql`hist.playedDate`, - lastPlayedAt: sql`hist.playedDate`, + `.as("next_entry"), + score: sql`null`.as("score"), + startedAt: sql`hist.playedDate`.as("startedAt"), + lastPlayedAt: sql`hist.playedDate`.as("lastPlayedAt"), completedAt: sql` case when ${nextEntryQ.pk} is null then hist.playedDate else null end - `, + `.as("completedAt"), // see https://github.com/drizzle-team/drizzle-orm/issues/3608 - updatedAt: sql`now()`, + updatedAt: sql`now()`.as("updatedAt"), }) .from(hist) .leftJoin(entries, valEqEntries) - .crossJoinLateral(nextEntryQ), + .leftJoinLateral(nextEntryQ, sql`true`), ) .onConflictDoUpdate({ target: [watchlist.profilePk, watchlist.showPk], diff --git a/api/src/controllers/profiles/nextup.ts b/api/src/controllers/profiles/nextup.ts index 4e8b6ac9..142ae634 100644 --- a/api/src/controllers/profiles/nextup.ts +++ b/api/src/controllers/profiles/nextup.ts @@ -90,6 +90,7 @@ export const nextup = new Elysia({ tags: ["profiles"] }) seasonNumber, episodeNumber, extraKind, + kind, ...entryCol } = getColumns(entries); @@ -100,9 +101,10 @@ export const nextup = new Elysia({ tags: ["profiles"] }) videos: entryVideosQ.videos, progress: mapProgress({ aliased: true }), // specials don't have an `episodeNumber` but a `number` field. - number: episodeNumber, + number: sql`${episodeNumber}`, // assign more restrained types to make typescript happy. + kind: sql`${kind}`, externalId: sql`${externalId}`, order: sql`${order}`, seasonNumber: sql`${seasonNumber}`, diff --git a/api/src/models/entry/index.ts b/api/src/models/entry/index.ts index bfdc54dc..d4a338a3 100644 --- a/api/src/models/entry/index.ts +++ b/api/src/models/entry/index.ts @@ -3,7 +3,6 @@ import { Episode, SeedEpisode } from "./episode"; import type { Extra } from "./extra"; import { MovieEntry, SeedMovieEntry } from "./movie-entry"; import { SeedSpecial, Special } from "./special"; -import type { UnknownEntry } from "./unknown-entry"; export const Entry = t.Union([Episode, MovieEntry, Special]); export type Entry = Episode | MovieEntry | Special; @@ -11,7 +10,7 @@ export type Entry = Episode | MovieEntry | Special; export const SeedEntry = t.Union([SeedEpisode, SeedMovieEntry, SeedSpecial]); export type SeedEntry = SeedEpisode | SeedMovieEntry | SeedSpecial; -export type EntryKind = Entry["kind"] | Extra["kind"] | UnknownEntry["kind"]; +export type EntryKind = Entry["kind"] | Extra["kind"]; export * from "./episode"; export * from "./movie-entry"; diff --git a/api/src/models/examples/dune-1984.ts b/api/src/models/examples/dune-1984.ts index 34084f77..b417ed7e 100644 --- a/api/src/models/examples/dune-1984.ts +++ b/api/src/models/examples/dune-1984.ts @@ -3,7 +3,6 @@ import type { Video } from "../video"; export const dune1984Video: Video = { id: "d1a62b87-9cfd-4f9c-9ad7-21f9b7fa6290", - slug: "dune-1984", path: "/video/Dune_1984/Dune (1984).mkv", rendering: "ea3a0f8f2f2c5b61a07f61e4e8d9f8e01b2b92bcbb6f5ed1151e1f61619c2c0f", part: null, diff --git a/api/src/models/examples/dune-2021.ts b/api/src/models/examples/dune-2021.ts index 08cb3499..1c6b9427 100644 --- a/api/src/models/examples/dune-2021.ts +++ b/api/src/models/examples/dune-2021.ts @@ -3,7 +3,6 @@ import type { Video } from "~/models/video"; export const duneVideo: Video = { id: "c9a0d02e-6b8e-4ac1-b431-45b022ec0708", - slug: "dune", path: "/video/Dune/Dune (2021).mkv", rendering: "f1953a4fb58247efb6c15b76468b6a9d13b4155b02094863b1a4f0c3fbb6db58", part: null, diff --git a/api/src/models/show.ts b/api/src/models/show.ts index ae5a0a43..5bcf6237 100644 --- a/api/src/models/show.ts +++ b/api/src/models/show.ts @@ -4,7 +4,8 @@ import { Movie } from "./movie"; import { Serie } from "./serie"; export const Show = t.Union([ - t.Composite([t.Object({ kind: t.Literal("movie") }), Movie]), - t.Composite([t.Object({ kind: t.Literal("serie") }), Serie]), - t.Composite([t.Object({ kind: t.Literal("collection") }), Collection]), + t.Intersect([t.Object({ kind: t.Literal("movie") }), Movie]), + t.Intersect([t.Object({ kind: t.Literal("serie") }), Serie]), + t.Intersect([t.Object({ kind: t.Literal("collection") }), Collection]), ]); +export type Show = typeof Show.static; diff --git a/api/src/models/watchlist.ts b/api/src/models/watchlist.ts index 5e072f6a..7ce5b557 100644 --- a/api/src/models/watchlist.ts +++ b/api/src/models/watchlist.ts @@ -7,6 +7,7 @@ export const WatchlistStatus = t.UnionEnum([ "dropped", "planned", ]); +export type WatchlistStatus = typeof WatchlistStatus.static; export const SerieWatchStatus = t.Object({ status: WatchlistStatus, diff --git a/api/tests/movies/get-movie.test.ts b/api/tests/movies/get-movie.test.ts index f9afa6fe..5e7b9ee9 100644 --- a/api/tests/movies/get-movie.test.ts +++ b/api/tests/movies/get-movie.test.ts @@ -159,7 +159,7 @@ describe("Get movie", () => { expect(body.videos).toBeArrayOfSize(bubble.videos!.length); expect(body.videos[0]).toMatchObject({ path: bubbleVideo.path, - slug: bubbleVideo.slug, + slug: bubble.slug, version: bubbleVideo.version, rendering: bubbleVideo.rendering, part: bubbleVideo.part,