Files
gerbeur/api/model/category.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

46 lines
1.4 KiB
TypeScript

/**
* Turns the rows the category service reads from `categories` into the {@link
* Category} the category routes send.
*
* @module
*/
import type { SQLOutputValue } from "node:sqlite";
import type { Category } from "../../shared/category.ts";
/** Returned by the category service's `SELECT`s on `categories`. */
export interface CategoryRow {
id: string;
slug: string;
name: string;
/** Sorts the categories, lowest first. */
position: number;
created_at: string;
updated_at: string | null;
[key: string]: SQLOutputValue;
}
/** Checks whether a row `node:sqlite` returned is a {@link CategoryRow}. */
export function isCategoryRow(obj: unknown): obj is CategoryRow {
return !!obj && typeof obj === "object" &&
"id" in obj && typeof obj.id === "string" &&
"slug" in obj && typeof obj.slug === "string" &&
"name" in obj && typeof obj.name === "string" &&
"position" in obj && typeof obj.position === "number" &&
"created_at" in obj && typeof obj.created_at === "string" &&
"updated_at" in obj &&
(typeof obj.updated_at === "string" || obj.updated_at === null);
}
/** Converts `row` to the {@link Category} the category routes send. */
export function categoryRowToApi(row: CategoryRow): Category {
return {
id: row.id,
slug: row.slug,
name: row.name,
position: row.position,
createdAt: new Date(row.created_at),
updatedAt: row.updated_at ? new Date(row.updated_at) : undefined,
};
}