// CITATIONS.md — the citation model's key tables, GENERATED from the schemas // (./schema.ts): the keys and their order from each member's zod shape, // required-or-not from the same shape, the prose from the *_FIELD_DOCS records. // // A pure renderer, the sibling of lib/fileSchemaDocs.ts (SITE.md, CHANNEL.md): // common/bin/file-schemas-docs.ts writes the file and citations/docs.test.ts // asserts the committed bytes are what this returns, so it cannot be edited by // hand. import type { z } from "zod"; import { cell } from "../settingsDocs"; import { AUDIO_CITATION_FIELD_DOCS, CITATION_COMMON_FIELD_DOCS, CITATION_PAD_FIELD_DOCS, CITATION_SET_FIELD_DOCS, CITATION_SET_FORMAT, CITATION_VERIFICATION_FIELD_DOCS, CITATIONS_VERSION, MAX_CITATION_SPAN_SECONDS, PAGE_CITATION_FIELD_DOCS, POST_CITATION_FIELD_DOCS, SOURCE_ARCHIVE_FIELD_DOCS, SOURCE_CITATION_FIELD_DOCS, SOURCE_FIELD_DOCS, VIDEO_CITATION_FIELD_DOCS, audioCitationSchema, citationSetSchema, pageCitationSchema, postCitationSchema, sourceArchiveSchema, sourceCitationSchema, sourceSchema, verificationSchema, videoCitationSchema, } from "./schema"; export const GENERATED_SCHEMA_DOC = ""; export const REGENERATE_SCHEMA_DOC = "Regenerate this file with " + "`pnpm --filter yt-dlp-transcript-common exec tsx bin/file-schemas-docs.ts`."; type Shape = Record; // Whether a key of a zod object shape may be left out. export function isOptionalKey(shape: Shape, key: string): boolean { return shape[key].safeParse(undefined).success; } // One key table, `Key | Required | Description`, in `docs` order; only the // keys `docs` names (a member's own keys, when the common ones are shared). export function renderKeyTable( out: string[], heading: string, shape: Shape, docs: Readonly>, ): void { out.push(heading); out.push(""); out.push("| Key | Required | Description |"); out.push("|---|---|---|"); for (const [key, text] of Object.entries(docs)) { if (!(key in shape)) throw new Error(`${heading}: ${key} is documented but not in the schema`); out.push(`| \`${key}\` | ${isOptionalKey(shape, key) ? "no" : "yes"} | ${cell(text)} |`); } out.push(""); } export function renderCitationsMarkdown(): string { const out: string[] = []; out.push("# Citations"); out.push(""); out.push(GENERATED_SCHEMA_DOC); out.push(""); out.push( "The one citation model, for every document that cites the corpus: a report " + "(`report.json` — see [REPORT.md](REPORT.md)), a standalone citation set " + `(\`${CITATION_SET_FORMAT}\`, below), and whatever comes next. The schema is ` + "`common/lib/citations/schema.ts`; the value rules are " + "`common/lib/citations/validate.ts`, which reports every problem with its JSON " + "path instead of stopping at the first.", ); out.push(""); out.push( "A citation is a discriminated union on `kind`: `video` and `audio` (a span of a " + "record's media), `post` (a post record), `source` (a sentence of a document under " + "review) and `page` (a web page outside the corpus). Every kind carries the common " + "fields. A container names its citations by id in a `citations` map, and its " + "documents in a `sources` map; an id is letters, digits and `_ . : -`, starting " + "with a letter or digit, at most 64, and no id names both a source and a citation. " + "Unknown keys are refused, and so is an unknown kind: a new kind is a new version.", ); out.push(""); out.push( "**Citing inline.** In any markdown a document carries, `[label](cite:)` cites " + "the citation `` (a link inside a code span or a fenced block is text, not a " + "citation). Citations are numbered per document by first appearance in reading " + "order — a citation cited again keeps its number — and each has the stable anchor " + "`#c-`.", ); out.push(""); out.push( "**Moments.** A `video` or `audio` citation opens the page " + "`/m///-/`, a `post` citation `/m///`; " + "`source` and `page` citations have no page of their own. `` and `` " + "are written with two decimals, always (`12.50`, never `12.5`), so a moment has " + "one URL; the pad is not part of it. `` and `` are single safe path " + "segments (letters, digits, `.`, `_`, `-`; a channel starts with a letter or " + "digit, an id may start with `-`). Moment keys are `common/lib/citations/moments.ts`.", ); out.push(""); out.push( `**Spans.** \`start\` is at least 0, \`end\` is after it (by at least 0.01 s), each \`pad\` ` + `is at least 0, and the span with its pad is at most ${MAX_CITATION_SPAN_SECONDS} s.`, ); out.push(""); out.push( "**Paths.** A still (`image`) or a saved document (`saved`) is relative to the " + "container's directory: `/`-separated, never absolute, with no empty, `.` or `..` " + "segment.", ); out.push(""); out.push(REGENERATE_SCHEMA_DOC); out.push(""); out.push("## Every kind"); out.push(""); renderKeyTable(out, "#### Common fields", videoCitationSchema.shape, CITATION_COMMON_FIELD_DOCS); renderKeyTable(out, "#### `verification`", verificationSchema.shape, CITATION_VERIFICATION_FIELD_DOCS); out.push("## The kinds"); out.push(""); renderKeyTable(out, '#### `"video"`', videoCitationSchema.shape, VIDEO_CITATION_FIELD_DOCS); renderKeyTable(out, '#### `"audio"`', audioCitationSchema.shape, AUDIO_CITATION_FIELD_DOCS); renderKeyTable( out, "#### `pad` (video, audio)", videoCitationSchema.shape.pad.unwrap().shape, CITATION_PAD_FIELD_DOCS, ); renderKeyTable(out, '#### `"post"`', postCitationSchema.shape, POST_CITATION_FIELD_DOCS); renderKeyTable(out, '#### `"source"`', sourceCitationSchema.shape, SOURCE_CITATION_FIELD_DOCS); renderKeyTable(out, '#### `"page"`', pageCitationSchema.shape, PAGE_CITATION_FIELD_DOCS); out.push("## Sources"); out.push(""); out.push( "The documents a `source` citation quotes, in a container's `sources` map by id. " + "A saved copy (`saved`) is the input stills are shot from and is never published.", ); out.push(""); renderKeyTable(out, "#### `sources.`", sourceSchema.shape, SOURCE_FIELD_DOCS); renderKeyTable(out, "#### `sources..archives[]`", sourceArchiveSchema.shape, SOURCE_ARCHIVE_FIELD_DOCS); out.push("## A citation set"); out.push(""); out.push( `Citations outside a report — what a converter of a sweep, an answer or a video ` + `manifest emits — are one JSON document, format \`"${CITATION_SET_FORMAT}"\`, ` + `version ${CITATIONS_VERSION}.`, ); out.push(""); renderKeyTable(out, "#### The document", citationSetSchema.shape, CITATION_SET_FIELD_DOCS); return out.join("\n"); }