v3: code quality pass

This commit is contained in:
khannurien
2026-03-24 18:47:05 +00:00
parent cd4076343b
commit c293f3e706
39 changed files with 1464 additions and 1555 deletions

View File

@@ -75,7 +75,7 @@ export interface UserRow {
* Type Guards
*/
export function isDumpRow(obj: Record<string, SQLOutputValue>): obj is DumpRow {
export function isDumpRow(obj: unknown): obj is DumpRow {
return !!obj &&
typeof obj === "object" &&
"id" in obj && typeof obj.id === "string" &&
@@ -102,7 +102,7 @@ export function isDumpRow(obj: Record<string, SQLOutputValue>): obj is DumpRow {
"is_private" in obj && typeof obj.is_private === "number";
}
export function isUserRow(obj: Record<string, SQLOutputValue>): obj is UserRow {
export function isUserRow(obj: unknown): obj is UserRow {
return !!obj &&
typeof obj === "object" &&
"id" in obj && typeof obj.id === "string" &&
@@ -214,18 +214,21 @@ export interface CommentRow {
}
export function isCommentRow(
obj: Record<string, SQLOutputValue>,
obj: unknown,
): obj is CommentRow {
return !!obj && typeof obj === "object" &&
typeof obj.id === "string" &&
typeof obj.dump_id === "string" &&
typeof obj.user_id === "string" &&
"id" in obj && typeof obj.id === "string" &&
"dump_id" in obj && typeof obj.dump_id === "string" &&
"user_id" in obj && typeof obj.user_id === "string" &&
"parent_id" in obj &&
(typeof obj.parent_id === "string" || obj.parent_id === null) &&
typeof obj.body === "string" &&
typeof obj.created_at === "string" &&
"body" in obj && typeof obj.body === "string" &&
"created_at" in obj && typeof obj.created_at === "string" &&
"updated_at" in obj &&
(typeof obj.updated_at === "string" || obj.updated_at === null) &&
typeof obj.deleted === "number" &&
typeof obj.author_username === "string" &&
"deleted" in obj && typeof obj.deleted === "number" &&
"author_username" in obj && typeof obj.author_username === "string" &&
"author_avatar_mime" in obj &&
(typeof obj.author_avatar_mime === "string" ||
obj.author_avatar_mime === null);
}
@@ -259,16 +262,20 @@ export interface PlaylistRow {
}
export function isPlaylistRow(
obj: Record<string, SQLOutputValue>,
obj: unknown,
): obj is PlaylistRow {
return !!obj && typeof obj.id === "string" &&
typeof obj.user_id === "string" &&
typeof obj.title === "string" &&
(typeof obj.slug === "string" || obj.slug === null) &&
return !!obj && typeof obj === "object" &&
"id" in obj && typeof obj.id === "string" &&
"user_id" in obj && typeof obj.user_id === "string" &&
"title" in obj && typeof obj.title === "string" &&
"slug" in obj && (typeof obj.slug === "string" || obj.slug === null) &&
"description" in obj &&
(typeof obj.description === "string" || obj.description === null) &&
typeof obj.is_public === "number" &&
typeof obj.created_at === "string" &&
"is_public" in obj && typeof obj.is_public === "number" &&
"created_at" in obj && typeof obj.created_at === "string" &&
"updated_at" in obj &&
(typeof obj.updated_at === "string" || obj.updated_at === null) &&
"image_mime" in obj &&
(typeof obj.image_mime === "string" || obj.image_mime === null);
}
@@ -300,15 +307,15 @@ export interface FollowRow {
}
export function isFollowRow(
obj: Record<string, SQLOutputValue>,
obj: unknown,
): obj is FollowRow {
return !!obj &&
typeof obj.id === "string" &&
typeof obj.follower_id === "string" &&
typeof obj.created_at === "string" &&
(obj.followed_user_id === null ||
return !!obj && typeof obj === "object" &&
"id" in obj && typeof obj.id === "string" &&
"follower_id" in obj && typeof obj.follower_id === "string" &&
"created_at" in obj && typeof obj.created_at === "string" &&
"followed_user_id" in obj && (obj.followed_user_id === null ||
typeof obj.followed_user_id === "string") &&
(obj.followed_playlist_id === null ||
"followed_playlist_id" in obj && (obj.followed_playlist_id === null ||
typeof obj.followed_playlist_id === "string");
}
@@ -326,15 +333,16 @@ export interface NotificationRow {
}
export function isNotificationRow(
obj: Record<string, SQLOutputValue>,
obj: unknown,
): obj is NotificationRow {
return !!obj && typeof obj === "object" &&
typeof obj.id === "string" &&
typeof obj.user_id === "string" &&
typeof obj.type === "string" &&
typeof obj.data === "string" &&
typeof obj.read === "number" &&
typeof obj.created_at === "string" &&
"id" in obj && typeof obj.id === "string" &&
"user_id" in obj && typeof obj.user_id === "string" &&
"type" in obj && typeof obj.type === "string" &&
"data" in obj && typeof obj.data === "string" &&
"read" in obj && typeof obj.read === "number" &&
"created_at" in obj && typeof obj.created_at === "string" &&
"source_key" in obj &&
(typeof obj.source_key === "string" || obj.source_key === null);
}
@@ -360,11 +368,12 @@ export interface InviteRow {
}
export function isInviteRow(
obj: Record<string, SQLOutputValue>,
obj: unknown,
): obj is InviteRow {
return !!obj && typeof obj === "object" &&
typeof obj.token === "string" &&
typeof obj.inviter_id === "string" &&
typeof obj.created_at === "string" &&
"token" in obj && typeof obj.token === "string" &&
"inviter_id" in obj && typeof obj.inviter_id === "string" &&
"created_at" in obj && typeof obj.created_at === "string" &&
"used_at" in obj &&
(obj.used_at === null || typeof obj.used_at === "string");
}

View File

@@ -310,7 +310,7 @@ export interface CreatePlaylistRequest {
export interface UpdatePlaylistRequest {
title?: string;
description?: string;
description?: string | null;
isPublic?: boolean;
}
@@ -428,40 +428,33 @@ export function isUpdateDumpRequest(obj: unknown): obj is UpdateDumpRequest {
* WebSockets
*/
// ── Client → Server ──────────────────────────────────────────────────────────
export interface PingMessage {
type: "ping";
}
export interface PongMessage {
type: "pong";
}
export interface VoteCastMessage {
type: "vote_cast";
dumpId: string;
userId: string;
}
export interface VoteAckMessageFailure {
type: "vote_ack";
dumpId: string;
success: false;
error: APIError;
}
export interface VoteAckMessageSuccess {
type: "vote_ack";
dumpId: string;
action: "cast" | "remove";
success: true;
voteCount: number;
error?: never;
}
export type VoteAckMessage = VoteAckMessageSuccess | VoteAckMessageFailure;
export interface VoteRemoveMessage {
type: "vote_remove";
dumpId: string;
}
export interface VotesUpdateMessage {
type: "votes_update";
dumpId: string;
voteCount: number;
}
export type ClientToServerMessage =
| PingMessage
| PongMessage
| VoteCastMessage
| VoteRemoveMessage;
// ── Server → Client ──────────────────────────────────────────────────────────
export interface OnlineUser {
userId: string;
@@ -474,6 +467,7 @@ export interface WelcomeMessage {
type: "welcome";
users: OnlineUser[];
myVotes: string[];
unreadNotificationCount: number;
}
export interface PresenceUpdateMessage {
@@ -481,14 +475,109 @@ export interface PresenceUpdateMessage {
users: OnlineUser[];
}
export interface PingMessage {
type: "ping";
export interface VotesUpdateMessage {
type: "votes_update";
dumpId: string;
voteCount: number;
voterId: string;
action: "cast" | "remove";
}
export interface PongMessage {
type: "pong";
export interface VoteAckMessage {
type: "vote_ack";
dumpId: string;
action: "cast" | "remove";
voteCount: number;
}
export interface DumpCreatedMessage {
type: "dump_created";
dump: Dump;
}
export interface DumpUpdatedMessage {
type: "dump_updated";
dump: Dump;
}
export interface DumpDeletedMessage {
type: "dump_deleted";
dumpId: string;
}
export interface PlaylistCreatedMessage {
type: "playlist_created";
playlist: Playlist;
}
export interface PlaylistUpdatedMessage {
type: "playlist_updated";
playlist: Playlist;
}
export interface PlaylistDeletedMessage {
type: "playlist_deleted";
playlistId: string;
userId: string;
}
export interface PlaylistDumpsUpdatedMessage {
type: "playlist_dumps_updated";
playlistId: string;
dumpIds: string[];
}
export interface UserUpdatedMessage {
type: "user_updated";
user: Omit<User, "passwordHash">;
}
export interface CommentCreatedMessage {
type: "comment_created";
comment: Comment;
}
export interface CommentUpdatedMessage {
type: "comment_updated";
comment: Comment;
}
export interface CommentDeletedMessage {
type: "comment_deleted";
commentId: string;
dumpId: string;
}
export interface NotificationCreatedMessage {
type: "notification_created";
notification: RawNotification;
}
export interface ErrorMessage {
type: "error";
message?: string;
}
export type ServerToClientMessage =
| PingMessage
| WelcomeMessage
| PresenceUpdateMessage
| VotesUpdateMessage
| VoteAckMessage
| DumpCreatedMessage
| DumpUpdatedMessage
| DumpDeletedMessage
| PlaylistCreatedMessage
| PlaylistUpdatedMessage
| PlaylistDeletedMessage
| PlaylistDumpsUpdatedMessage
| UserUpdatedMessage
| CommentCreatedMessage
| CommentUpdatedMessage
| CommentDeletedMessage
| NotificationCreatedMessage
| ErrorMessage;
/**
* Follows
*/
@@ -568,3 +657,8 @@ export interface Notification {
read: boolean;
createdAt: Date;
}
/** Wire format — createdAt arrives as an ISO string over JSON. */
export type RawNotification = Omit<Notification, "createdAt"> & {
createdAt: string;
};