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:
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* Backlinks between dumps: which dumps a dump's description and comments link
|
||||
* to, and the dumps related to one through them.
|
||||
* Records which dumps a dump's note and comments link to, and finds the dumps
|
||||
* related to one through those links. The dump and comment services keep the
|
||||
* links up to date, and the dump page lists the related dumps.
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
@@ -15,21 +16,19 @@ import {
|
||||
import { UUID_RE } from "../lib/slugify.ts";
|
||||
|
||||
/**
|
||||
* An http(s) URL in free text — stops at whitespace or markdown-link
|
||||
* delimiters (so `[text](url)` and `<url>` don't swallow the closing char).
|
||||
* Matches an http(s) URL in Markdown. It stops at the delimiters of
|
||||
* `[text](url)` and `<url>`, which would otherwise end up in the URL.
|
||||
*/
|
||||
const URL_RE = /https?:\/\/[^\s)\]<>"']+/g;
|
||||
|
||||
/**
|
||||
* A link to another dump's page on this same site (e.g. pasted from
|
||||
* the address bar) — host-agnostic, since the configured public URL can differ
|
||||
* between dev/prod/reverse-proxy setups. Matches `/dumps/{slug-or-id}`,
|
||||
* ignoring any trailing path/query/hash (so `/edit` or `#comment-x` links
|
||||
* still resolve to the same target dump).
|
||||
* Matches the path of a dump's page, `/dumps/{slug-or-id}`, and ignores what
|
||||
* follows it, so an `/edit` or `#comment-x` link counts too. The host isn't
|
||||
* checked, since the site's public URL differs between setups.
|
||||
*/
|
||||
const DUMP_PATH_RE = /^\/dumps\/([^/?#]+)/;
|
||||
|
||||
/** Returns `url` as the URL parser normalises it, or null when it isn't one. */
|
||||
/** Returns `url` as the URL parser normalizes it, or null when it isn't one. */
|
||||
function canonicalize(url: string): string | null {
|
||||
try {
|
||||
return new URL(url).toString();
|
||||
@@ -38,7 +37,7 @@ function canonicalize(url: string): string | null {
|
||||
}
|
||||
}
|
||||
|
||||
/** Resolves a same-site `/dumps/{slug-or-id}` path segment to a dump id. */
|
||||
/** Returns the id of the dump with id or slug `idOrSlug`, or null. */
|
||||
function resolveDumpPathId(idOrSlug: string): string | null {
|
||||
const row = UUID_RE.test(idOrSlug)
|
||||
? db.prepare(`SELECT id FROM dumps WHERE id = ?;`).get(idOrSlug)
|
||||
@@ -47,11 +46,11 @@ function resolveDumpPathId(idOrSlug: string): string | null {
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-derives the backlinks contributed by one text source (a dump's own
|
||||
* `comment` field, or a single comment's `body`) and persist them. Always
|
||||
* deletes the source's previous contribution first, so editing/clearing the
|
||||
* text correctly removes stale links — unlike `linkAttachments`/`notifyMentions`,
|
||||
* which are purely additive.
|
||||
* Replaces the backlinks from one text, a dump's note (`dump_comment`) or one
|
||||
* of its comments (`comment`), with the links its current `text` holds. A link
|
||||
* counts when it is a dump's page on this site or the exact URL a dump was
|
||||
* posted with. The dump and comment services call it on every save, and with
|
||||
* an empty `text` when a comment is deleted.
|
||||
*/
|
||||
export function relinkBacklinks(
|
||||
sourceType: "dump_comment" | "comment",
|
||||
@@ -88,7 +87,7 @@ export function relinkBacklinks(
|
||||
for (const row of rows) targetIds.add(row.id);
|
||||
}
|
||||
|
||||
targetIds.delete(fromDumpId); // no self-links
|
||||
targetIds.delete(fromDumpId);
|
||||
|
||||
transaction(() => {
|
||||
db.prepare(
|
||||
@@ -107,7 +106,8 @@ export function relinkBacklinks(
|
||||
|
||||
/**
|
||||
* Returns the dumps linked to or from `dumpId`, newest first and at most 20,
|
||||
* without the private ones the requesting user can't see.
|
||||
* without the private ones the requesting user can't see. It doesn't check
|
||||
* `dumpId` itself: `GET /api/dumps/:dumpId/related` does that first.
|
||||
*/
|
||||
export function getRelatedDumps(
|
||||
dumpId: string,
|
||||
|
||||
Reference in New Issue
Block a user