/** * How a release got its name, as releases.source stores it (a smallint; voyager 3, migration 6000). The names are * what the edge, the spot inspector and the API use; the numbers never change meaning once written. */ export const SOURCES = { /** A name the subject announces ("name" yEnc (k/N)). */ subject: 1, /** The post's files, named after the first file (no release name in the subject). */ post: 2, /** The files' shared stem. */ stem: 3, /** A Spotnet spot's files that matched no release. */ spot: 4, /** An NZB's files. */ nzb: 5, /** One poster's files that differ only by a number sequence, or share a prefix (voyager 3). */ album: 6, /** An obfuscated post's stem, read from the yEnc body (identity.ts: weak). */ 'weak-stem': 7, } as const export type Source = keyof typeof SOURCES const NAMES = Object.fromEntries( Object.entries(SOURCES).map(([name, code]) => [code, name]) ) as Record /** The code of a source name; throws for an unknown one (a typo would write a wrong number for good). */ export function sourceCode(name: string): number { const code = SOURCES[name as Source] if (code === undefined) throw new Error(`Unknown release source "${name}"`) return code } /** The name of a stored code; "unknown" for one not listed (never written, but a row might be read by an older build). */ export function sourceName(code: number | string | null | undefined): Source | 'unknown' { return NAMES[Number(code)] ?? 'unknown' }