spb/cancerindex
Public
TypeScript 97.2%
SQL 1.5%
CSS 0.6%
JavaScript 0.5%
1import { z } from 'zod';23/** Connector manifest (CLAUDE.md §9). Seeds the `sources` table; drives the public /source pages. */4export const ConnectorManifest = z.object({5 id: z.string().regex(/^[a-z0-9-]+$/),6 name: z.string(),7 organization: z.string(),8 category: z.enum(['terminology', 'genes', 'genomics', 'variants', 'epidemiology', 'trials', 'literature', 'drugs', 'regulatory', 'evidence', 'pathways', 'funding', 'guidelines']),9 tier: z.number().int().min(0).max(11),10 description: z.string(),11 homepage: z.string().url(),12 docsUrl: z.string().url().optional(),13 termsUrl: z.string().url().optional(),14 access: z.object({15 type: z.enum(['api', 'bulk', 'rss', 'ftp', 'graphql', 'rest', 'scrape', 'manual']),16 auth: z.enum(['none', 'api_key', 'oauth', 'account', 'controlled']),17 baseUrl: z.string().url().optional(),18 }),19 license: z.string(),20 licenseStatus: z.enum(['review', 'approved', 'restricted', 'blocked']),21 commercialUse: z.enum(['allowed', 'restricted', 'prohibited', 'unknown']),22 redistribution: z.enum(['allowed', 'attribution', 'restricted', 'prohibited', 'unknown']),23 attribution: z.string().optional(),24 termsReviewedAt: z.string().optional(), // YYYY-MM-DD, human review date25 termsNotes: z.string().optional(),26 updateFrequency: z.string(),27 expectedLatency: z.string().optional(),28 supportsIncrementalSync: z.boolean(),29 entities: z.array(z.string()),30 metrics: z.array(z.string()).default([]),31 rateLimits: z.object({32 requestsPerSecond: z.number().positive().optional(),33 requestsPerMinute: z.number().positive().optional(),34 maxConcurrency: z.number().int().positive().default(1),35 notes: z.string().optional(),36 }),37 retryPolicy: z38 .object({39 maxRetries: z.number().int().min(0).default(4),40 baseDelayMs: z.number().int().min(0).default(500),41 maxDelayMs: z.number().int().min(0).default(30_000),42 })43 .default({ maxRetries: 4, baseDelayMs: 500, maxDelayMs: 30_000 }),44 rawRetention: z.enum(['full', 'summary', 'none']).default('full'),45 schemaVersion: z.string().default('1'),46 documentationVerifiedAt: z.string(), // YYYY-MM-DD, the date official docs were checked (CLAUDE.md §224-225)47 owner: z.string().default('cancerindex'),48 status: z.enum(['planned', 'active', 'paused', 'awaiting_credentials', 'review', 'retired']),49 schedule: z.string().optional(), // cron expression for the worker scheduler50 anomalyGuard: z51 .object({52 /** Refuse to mark records source_missing when the fetched count drops below this ratio of the previous run. */53 minRatioOfPrevious: z.number().min(0).max(1).default(0.5),54 })55 .default({ minRatioOfPrevious: 0.5 }),56 /**57 * Mid-run cursor checkpoint (CLAUDE.md §90): `connector_cursors.cursor` is persisted after this many58 * upserted records (or every 60 s) whenever `ctx.cursor` changed, so a killed run resumes from the59 * last completed page instead of from the start of the run. See docs/connectors/README.md.60 */61 checkpointEvery: z.number().int().positive().default(2000),62});63export type ConnectorManifest = z.infer<typeof ConnectorManifest>;6465export function defineManifest(m: z.input<typeof ConnectorManifest>): ConnectorManifest {66 return ConnectorManifest.parse(m);67}68