Files
gerbeur/shared/dump.ts
khannurien f3f07ed1e3
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 49s
Build and Deploy Static Documentation Site / build-and-push (push) Successful in 22s
v3: every doc opens with the role, then the mechanism, then the details — the docs were written bottom-up: what a thing does first ("Declares the three contexts WSProvider fills…"), details that only make sense once its purpose is known next, and the reason it exists last ("…so that a feed doesn't re-render"), so a reader had nothing to hang the first sentences on. The README's rule caused it, since a module doc "opened with what the module does"; it now opens with the module's role in the app, and the value-side phrasing of a type ("Returned to Y by X") comes once the role is said, with WSContext as the before and after. WSContext.ts is rewritten that way throughout, and so are the docs elsewhere that buried their purpose in a trailing "so that" (the migrations, og, image-proxy, public-address, useFading, useFeedCache, the drafts), opened on a generic verb that hid it ("Holds", "Declares", "Loads", "Defines", "Types": the contexts and providers, the shared types, the models, the hooks), or were label fragments ("The page of a dump…", "A row of tabs."). A function whose verb is its action ("Stores session"), a props or result type, and a protocol message ("Sent by X when Y") already say their role and are left as they are. Only comments changed, and the catalogs only move their references; deno fmt, deno lint and doc:lint are clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:42:08 +00:00

140 lines
4.0 KiB
TypeScript

/**
* A dump is what users post: a link or a file, with a title and a note. This
* module types it 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
*/
/**
* 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 {
/**
* The provider that read the page: `youtube`, `bandcamp`, `soundcloud` or
* `generic`. The player picks how to play the link by it.
*/
type: string;
url: string;
siteName?: string;
title?: string;
description?: string;
/**
* A real preview image (og:image, a large content image, a video still).
* Never a favicon — an absent value means "this page offers no artwork",
* which is what makes the generated placeholder possible.
*/
thumbnailUrl?: string;
/** Used as the placeholder's glyph. */
faviconUrl?: string;
/** The page's declared brand color, normalized to `#rrggbb`. */
accentColor?: string;
/** Set for a YouTube link only. */
videoId?: string;
embedUrl?: string;
}
/**
* 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 {
id: string;
kind: "url" | "file";
title: string;
/** Made from the title. The dump's page URL takes it in place of the id. */
slug?: string;
/** The poster's note on the dump, in Markdown. */
comment?: string;
userId: string;
createdAt: Date;
updatedAt?: Date;
url?: string;
richContent?: RichContent;
fileName?: string;
fileMime?: string;
fileSize?: number;
voteCount: number;
/** Deleted comments are not counted. */
commentCount: number;
isPrivate: boolean;
/** Set when the poster uploaded a custom thumbnail. */
thumbnailMime?: string;
categoryIds?: string[];
}
/**
* 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 {
id: string;
slug?: string;
title: string;
/** The poster's username. */
username: string;
createdAt: Date;
voteCount: number;
commentCount: number;
}
/**
* Returned by `GET /api/preview` to the create form, which shows it before the
* link is posted.
*/
export interface LinkPreview {
/**
* 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;
/**
* Null only for a link that doesn't parse, which the route rejects before
* fetching anyway.
*/
richContent: RichContent | null;
}
/**
* 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 {
url: string;
/** Replaces the title read from the page, when the poster edited it. */
title?: string;
comment?: string;
isPrivate?: boolean;
categoryIds?: string[];
}
/**
* 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 {
/** Ignored for a file dump. A changed link has its preview fetched again. */
url?: string;
title?: string;
/** `null` removes the note. */
comment?: string | null;
isPrivate?: boolean;
/** Replaces the current categories. */
categoryIds?: string[];
}
/**
* 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 {
id: string;
}