v3: comments say what the code can't — the documentation pass wanted JSDoc on every export and every field of an exported interface, which filled the code with restatements ("The dump.", "Whether it is loading.", "The props of X.", "Renders the X.") and module docs that told a reader nothing ("The hot and new feeds, and the props every main feed of the index takes."); some were wrong as well as vague. Every comment in api/, shared/ and src/ is rewritten under one rule, now stated in the README's "Comments" section: every module and export still has a JSDoc, and it gives context (who uses it and where), usage (what a caller must know: a precondition, what it leaves to the caller, a side effect) or its place in the chain (where its input comes from, where its output goes); a field, parameter, private function or local gets one only when it adds to its parent's, and context about a whole type goes on the type; a type's doc is phrased from the value's side ("Returned to Y by X when Z", "Passed to Y by X"); full sentences, as short as they can be, no label-colon-list; // goes wherever the code isn't obvious and says why, and the section banners and narration go. Every claim was checked against the code, which turned up some forty comments that were wrong: DumpCard's claimed it was only used by the hot and new feeds, the WebSocket welcome claimed to be a socket's first message (presence comes first), UpdateUserRequest said null removes a description (it keeps it), deletedDumpIds leaves out that it also holds dumps made private, THEME_COLOR claimed to mirror index.html's, and the like. The routers' JSDoc now says where they are mounted and what becomes of a thrown APIException. doc-lint.ts lets a field without a doc through; a module or export without one still fails. No code changes; 204 backend and 628 frontend tests pass, and doc:lint, deno check, deno lint and tsc are clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
khannurien
2026-09-29 14:31:14 +00:00
parent 42e0356d21
commit 0d4e344d77
287 changed files with 5679 additions and 5135 deletions

View File

@@ -1,23 +1,26 @@
/**
* Dumps — the links and files users post — their link previews, and the bodies
* that post and edit them.
* Defines the dump, the link or file a user posts, as the API sends it, with
* its link preview and the bodies that create and edit it. The backend's
* `dump-service.ts` produces these types and the frontend's dump pages and
* create form consume them.
*
* @module
*/
/** What a link's page says about itself, as its preview card shows it. */
/**
* Built by a provider of `rich-content-service.ts` when a link is posted or
* previewed, and stored with the dump as JSON. The frontend's
* `RichContentCard` and player read it.
*/
export interface RichContent {
/**
* Which provider read it: `youtube`, `bandcamp`, `soundcloud` or `generic`.
* The provider that read the page: `youtube`, `bandcamp`, `soundcloud` or
* `generic`. The player picks how to play the link by it.
*/
type: string;
/** The link the preview is of. */
url: string;
/** The site's name. */
siteName?: string;
/** The page's title. */
title?: string;
/** The page's description. */
description?: string;
/**
* A real preview image (og:image, a large content image, a video still).
@@ -25,120 +28,112 @@ export interface RichContent {
* which is what makes the generated placeholder possible.
*/
thumbnailUrl?: string;
/** The page's own icon, used as the placeholder's glyph. */
/** Used as the placeholder's glyph. */
faviconUrl?: string;
/** The page's declared brand color, normalized to `#rrggbb`. */
accentColor?: string;
/** The YouTube video's id, for a YouTube link. */
/** Set for a YouTube link only. */
videoId?: string;
/** The URL of the page's embeddable player, if it has one. */
embedUrl?: string;
}
/** A dump: a link or a file a user posted. */
/**
* Returned by every endpoint that answers dumps, and pushed to clients by the
* `dump_created` and `dump_updated` socket messages. A URL dump has `url` and
* `richContent`, a file dump the `file…` fields. A private dump reaches its
* poster only.
*/
export interface Dump {
/** The dump's id. */
id: string;
/** `url` for a link, `file` for an upload. */
kind: "url" | "file";
/** The dump's title. */
title: string;
/** The dump's URL slug. */
/** Made from the title. The dump's page URL takes it in place of the id. */
slug?: string;
/** The poster's description, in Markdown. */
/** The poster's note on the dump, in Markdown. */
comment?: string;
/** The poster's id. */
userId: string;
/** When the dump was posted. */
createdAt: Date;
/** When the dump was last edited, if ever. */
updatedAt?: Date;
/** The link, for a URL dump. */
url?: string;
/** The link's preview, for a URL dump. */
richContent?: RichContent;
/** The uploaded file's name, for a file dump. */
fileName?: string;
/** The uploaded file's type, for a file dump. */
fileMime?: string;
/** The uploaded file's size in bytes, for a file dump. */
fileSize?: number;
/** How many users upvoted the dump. */
voteCount: number;
/** How many comments it has, not counting deleted ones. */
/** Deleted comments are not counted. */
commentCount: number;
/** Whether only the poster sees the dump. */
isPrivate: boolean;
/** The uploaded thumbnail's type, undefined without one. */
/** Set when the poster uploaded a custom thumbnail. */
thumbnailMime?: string;
/** The ids of the categories it is filed under. */
categoryIds?: string[];
}
/**
* An existing dump pointing at the same URL, as shown by the create form's
* duplicate hint (`GET /api/dumps/by-url`). Display-only projection — see
* `findDumpsByUrl`.
* Returned by `GET /api/dumps/by-url` for each dump already posted with the
* link being typed, and shown by the create form's `DuplicateNotice`. It holds
* only what the notice displays.
*/
export interface DumpUrlMatch {
/** The dump's id. */
id: string;
/** The dump's URL slug. */
slug?: string;
/** The dump's title. */
title: string;
/** The poster's name. */
/** The poster's username. */
username: string;
/** When the dump was posted. */
createdAt: Date;
/** How many users upvoted the dump. */
voteCount: number;
/** How many comments it has. */
commentCount: number;
}
/**
* `GET /api/preview`. `reached` is separate because a failed fetch still yields
* a usable stub (hostname only): without it the create form can't tell "this
* page has no preview" from "this link is dead".
* Returned by `GET /api/preview` to the create form, which shows it before the
* link is posted.
*/
export interface LinkPreview {
/** Whether the page could be fetched. */
/**
* False when the page couldn't be fetched. `richContent` is then a stub
* holding the host name, and this flag is how the form tells a dead link
* from a page with no preview.
*/
reached: boolean;
/** The preview, null when there is none at all. */
/**
* Null only for a link that doesn't parse, which the route rejects before
* fetching anyway.
*/
richContent: RichContent | null;
}
/** The JSON body of `POST /api/dumps`, which posts a link. */
/**
* Passed by the create form to `POST /api/dumps` as a JSON body, to post a
* link. A file is posted as a multipart form instead.
*/
export interface CreateUrlDumpRequest {
/** The link. */
url: string;
/** Overrides the title scraped from the page — the poster edited the preview. */
/** Replaces the title read from the page, when the poster edited it. */
title?: string;
/** The poster's description, in Markdown. */
comment?: string;
/** Whether only the poster will see it. */
isPrivate?: boolean;
/** The ids of the categories to file it under. */
categoryIds?: string[];
}
/** The body of `PUT /api/dumps/:dumpId`: the fields to change. */
/**
* Passed to `PUT /api/dumps/:dumpId` by the edit page, and by the dump page's
* inline title edit. A field left out stays as it is.
*/
export interface UpdateDumpRequest {
/** The new link, for a URL dump. */
/** Ignored for a file dump. A changed link has its preview fetched again. */
url?: string;
/** The new title. */
title?: string;
/** `null` removes the note; leaving it out keeps it. */
/** `null` removes the note. */
comment?: string | null;
/** Whether only the poster will see it. */
isPrivate?: boolean;
/** The ids of the categories to file it under, replacing the current ones. */
/** Replaces the current categories. */
categoryIds?: string[];
}
/** `POST /api/attachments`: the id the uploaded image is served under. */
/**
* Returned by `POST /api/attachments` to the text editor, which inserts the
* image into the body as a link to `GET /api/attachments/:id`.
*/
export interface AttachmentResponse {
/** The attachment's id, for `GET /api/attachments/:attachmentId`. */
id: string;
}