spb/cancerindex
Public
TypeScript 97.2%
SQL 1.5%
CSS 0.6%
JavaScript 0.5%
1/**2 * CancerIndex stable public identifiers (CLAUDE.md §6).3 *4 * CI-CAN-00000001 cancer entity5 * CI-GENE-00000001 gene6 * CI-VAR-00000001 variant7 * CI-DRUG-00000001 drug8 * CI-TRIAL-00000001 clinical trial9 * CI-PUB-00000001 publication10 * CI-BIO-00000001 biomarker11 * CI-STUDY-00000001 genomic study / cohort12 * CI-METRIC-00000001 metric definition13 * CI-SOURCE-00000001 source14 * CI-ORG-00000001 organization15 * CI-TRT-00000001 treatment / regimen16 * CI-ANAT-00000001 anatomical site17 * CI-GEO-00000001 geography18 * CI-PROV-00000001 provenance record19 *20 * IDs are minted from a database sequence per namespace (see @cancerindex/database `mintId`) and21 * are never reused. Database auto-increment integers are never exposed publicly.22 */23export const ID_NAMESPACES = [24 'CAN',25 'GENE',26 'VAR',27 'DRUG',28 'TRIAL',29 'PUB',30 'BIO',31 'STUDY',32 'METRIC',33 'SOURCE',34 'ORG',35 'TRT',36 'ANAT',37 'GEO',38 'PROV',39 'EDGE',40 'RANK',41] as const;42export type IdNamespace = (typeof ID_NAMESPACES)[number];4344export const ID_WIDTH = 8;4546export function formatId(ns: IdNamespace, n: number): string {47 if (!Number.isInteger(n) || n < 1) throw new Error(`invalid id number ${n}`);48 return `CI-${ns}-${String(n).padStart(ID_WIDTH, '0')}`;49}5051const ID_RE = /^CI-([A-Z]+)-(\d{8,})$/;5253export function parseId(id: string): { ns: IdNamespace; n: number } | null {54 const m = ID_RE.exec(id);55 if (!m) return null;56 const ns = m[1] as IdNamespace;57 if (!ID_NAMESPACES.includes(ns)) return null;58 return { ns, n: Number(m[2]) };59}6061export function isCiId(id: string, ns?: IdNamespace): boolean {62 const p = parseId(id);63 return !!p && (ns ? p.ns === ns : true);64}6566/** Ingest run identifiers: ING-CLINICALTRIALS-20260908-000019 (CLAUDE.md §176). */67export function formatRunId(connectorId: string, date: Date, seq: number): string {68 const d = date.toISOString().slice(0, 10).replace(/-/g, '');69 return `ING-${connectorId.toUpperCase().replace(/[^A-Z0-9]/g, '')}-${d}-${String(seq).padStart(6, '0')}`;70}71