v3: shared/ is documented like api/ — deno doc --lint over api/ and shared/ goes from 385 errors to none — every module of shared/ opens with a /** … @module */ block; every export and every field of an exported interface has JSDoc, each request body naming the endpoint that takes it and each response its endpoint; the // comments that described declarations (the mention pattern, the permission union and table, four socket messages, the error message's optimistic-action context) are /** */ now, and the socket protocol's heading comment, which sat on no declaration, is its module doc. The lint wanted explicit types on MENTION_RE and DUMP_MAX_FILE_SIZE_BYTES. Along the way: the chat edit is allowed to moderators too, which the new ChatEditMessage doc says; UpdateUserRequest says that PATCH /api/users/me takes only its description and email; playlist-service's reorderPlaylist @throws is rewrapped. mod.ts and the README no longer call shared/ undocumented. No behaviour changes; 205 backend tests pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,17 @@
|
||||
/**
|
||||
* Users as the API shows them, their roles, and the bodies and answers of
|
||||
* sign-up, sign-in, profile edits and invites.
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
/** What a user may do beyond the basics; see `ROLE_PERMISSIONS`. */
|
||||
export type Role = "user" | "moderator" | "admin";
|
||||
|
||||
/** Every {@link Role}, least to most privileged. */
|
||||
export const ROLES: readonly Role[] = ["user", "moderator", "admin"];
|
||||
|
||||
/** Checks whether `value` is a {@link Role}. */
|
||||
export function isRole(value: unknown): value is Role {
|
||||
return typeof value === "string" &&
|
||||
(ROLES as readonly string[]).includes(value);
|
||||
@@ -9,69 +19,111 @@ export function isRole(value: unknown): value is Role {
|
||||
|
||||
/** A user as anyone may see them: no password hash, no email. */
|
||||
export interface PublicUser {
|
||||
/** The user's id. */
|
||||
id: string;
|
||||
/** The user's name, fixed at sign-up. */
|
||||
username: string;
|
||||
/** The user's role. */
|
||||
role: Role;
|
||||
/** When the user signed up. */
|
||||
createdAt: Date;
|
||||
/** When the user last changed, if ever. */
|
||||
updatedAt?: Date;
|
||||
/** The avatar's type, undefined without one. */
|
||||
avatarMime?: string;
|
||||
/** The user's description of themselves, in Markdown. */
|
||||
description?: string;
|
||||
/** The name of whoever invited them. */
|
||||
invitedByUsername?: string;
|
||||
}
|
||||
|
||||
/** A user as they see themselves: everything but the password hash. */
|
||||
export interface SelfUser extends PublicUser {
|
||||
/** The address password resets are sent to. */
|
||||
email: string;
|
||||
}
|
||||
|
||||
/** A match for the @mention autocomplete. */
|
||||
export interface UserSuggestion {
|
||||
/** The user's id. */
|
||||
id: string;
|
||||
/** The user's name. */
|
||||
username: string;
|
||||
/** The avatar's type, null without one. */
|
||||
avatarMime: string | null;
|
||||
}
|
||||
|
||||
/** Someone a user invited, directly or down the line. */
|
||||
export interface Invitee {
|
||||
/** The user's id. */
|
||||
id: string;
|
||||
/** The user's name. */
|
||||
username: string;
|
||||
/** The avatar's type, undefined without one. */
|
||||
avatarMime?: string;
|
||||
/** The id of whoever invited them, to build the tree. */
|
||||
invitedById: string;
|
||||
/** When they signed up. */
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
/** The body of `POST /api/users/login`. */
|
||||
export interface LoginUserRequest {
|
||||
/** The user's name. */
|
||||
username: string;
|
||||
/** The user's password. */
|
||||
password: string;
|
||||
}
|
||||
|
||||
/** The body of `POST /api/users/register`. */
|
||||
export interface RegisterUserRequest {
|
||||
/** The name to sign up with. */
|
||||
username: string;
|
||||
/** The password to sign up with. */
|
||||
password: string;
|
||||
/** The invite's token. */
|
||||
inviteToken: string;
|
||||
/** The address password resets will be sent to. */
|
||||
email: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fields of a user to change. `PATCH /api/users/me` takes only `description`
|
||||
* and `email` from it; the role and the password change through their own
|
||||
* endpoints.
|
||||
*/
|
||||
export interface UpdateUserRequest {
|
||||
/** The new name. */
|
||||
username?: string;
|
||||
/** The new password. */
|
||||
password?: string;
|
||||
/** The new role. */
|
||||
role?: Role;
|
||||
/** The new description; `null` removes it. */
|
||||
description?: string | null;
|
||||
/** The new email address. */
|
||||
email?: string;
|
||||
}
|
||||
|
||||
/** The session token, plus the media-only one for file and thumbnail URLs. */
|
||||
export interface AuthTokens {
|
||||
/** The session token, sent as a bearer token. */
|
||||
token: string;
|
||||
/** The token media URLs carry as `?token=`, good for nothing else. */
|
||||
mediaToken: string;
|
||||
}
|
||||
|
||||
/** What sign-up and sign-in answer: the user and their tokens. */
|
||||
export interface AuthResponse extends AuthTokens {
|
||||
/** The signed-in user. */
|
||||
user: SelfUser;
|
||||
}
|
||||
|
||||
/** `GET /api/users/me/media-token`: a fresh media token. */
|
||||
export type MediaTokenResponse = Pick<AuthTokens, "mediaToken">;
|
||||
|
||||
/** `POST /api/invites`: the new invite's token. */
|
||||
export interface InviteResponse {
|
||||
/** The token, which the invite link carries. */
|
||||
token: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user